diff options
Diffstat (limited to 'usr/src/cmd/mdb/common')
25 files changed, 2350 insertions, 658 deletions
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c index 93bbece1c8..761b448f4f 100644 --- a/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c +++ b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include <kmdb/kmdb_kvm.h> @@ -546,6 +545,7 @@ kmt_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { kmt_data_t *kmt = mdb.m_target->t_data; struct utsname uts; + char uuid[37]; kreg_t tt; if (mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT, &uts, sizeof (uts), @@ -561,6 +561,17 @@ kmt_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) mdb_printf("operating system: %s %s (%s)\n", uts.release, uts.version, uts.machine); + if (mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT, uuid, sizeof (uuid), + "genunix", "dump_osimage_uuid") != sizeof (uuid)) { + warn("failed to read 'dump_osimage_uuid' string from kernel\n"); + (void) strcpy(uuid, "(error)"); + } else if (*uuid == '\0') { + (void) strcpy(uuid, "(not set)"); + } else if (uuid[36] != '\0') { + (void) strcpy(uuid, "(invalid)"); + } + mdb_printf("image uuid: %s\n", uuid); + if (kmt->kmt_cpu != NULL) { mdb_printf("CPU-specific support: %s\n", kmt_cpu_name(kmt->kmt_cpu)); diff --git a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c index 5d0da621cd..4b368e357b 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -485,9 +484,18 @@ kt_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) if (kt->k_dumphdr) { dumphdr_t *dh = kt->k_dumphdr; + mdb_printf("image uuid: %s\n", dh->dump_uuid[0] != '\0' ? + dh->dump_uuid : "(not set)"); mdb_printf("panic message: %s\n", dh->dump_panicstring); kt->k_dump_print_content(dh, kt->k_dumpcontent); + } else { + char uuid[37]; + + if (mdb_readsym(uuid, 37, "dump_osimage_uuid") == 37 && + uuid[36] == '\0') { + mdb_printf("image uuid: %s\n", uuid); + } } return (DCMD_OK); diff --git a/usr/src/cmd/mdb/common/mdb/mdb_modapi.c b/usr/src/cmd/mdb/common/mdb/mdb_modapi.c index 670df6a2e2..509ffeb6ac 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.c @@ -18,13 +18,11 @@ * * CDDL HEADER END */ + /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <mdb/mdb_modapi.h> #include <mdb/mdb_module.h> #include <mdb/mdb_string.h> @@ -189,6 +187,12 @@ mdb_lookup_by_addr(uintptr_t addr, uint_t flags, char *buf, buf, nbytes, sym, NULL)); } +int +mdb_getareg(mdb_tid_t tid, const char *rname, mdb_reg_t *rp) +{ + return (mdb_tgt_getareg(mdb.m_target, tid, rname, rp)); +} + u_longlong_t mdb_strtoull(const char *s) { @@ -744,6 +748,51 @@ mdb_get_xdata(const char *name, void *buf, size_t nbytes) } /* + * Private callback structure for implementing mdb_object_iter, below. + */ +typedef struct { + mdb_object_cb_t oi_cb; + void *oi_arg; + int oi_rval; +} object_iter_arg_t; + +/*ARGSUSED*/ +static int +mdb_object_cb(void *data, const mdb_map_t *map, const char *fullname) +{ + object_iter_arg_t *arg = data; + mdb_object_t obj; + + if (arg->oi_rval != 0) + return (0); + + bzero(&obj, sizeof (obj)); + obj.obj_base = map->map_base; + obj.obj_name = strbasename(map->map_name); + obj.obj_size = map->map_size; + obj.obj_fullname = fullname; + + arg->oi_rval = arg->oi_cb(&obj, arg->oi_arg); + + return (0); +} + +int +mdb_object_iter(mdb_object_cb_t cb, void *data) +{ + object_iter_arg_t arg; + + arg.oi_cb = cb; + arg.oi_arg = data; + arg.oi_rval = 0; + + if (mdb_tgt_object_iter(mdb.m_target, mdb_object_cb, &arg) != 0) + return (-1); + + return (arg.oi_rval); +} + +/* * Private structure and function for implementing mdb_dumpptr on top * of mdb_dump_internal */ diff --git a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h index 338c56c55f..aa7e8409b6 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h +++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h @@ -18,16 +18,14 @@ * * CDDL HEADER END */ + /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _MDB_MODAPI_H #define _MDB_MODAPI_H -#pragma ident "%Z%%M% %I% %E% SMI" - /* * MDB Module API * @@ -164,6 +162,13 @@ typedef struct mdb_pipe { size_t pipe_len; /* Array length */ } mdb_pipe_t; +typedef struct mdb_object { + const char *obj_name; /* name of object */ + const char *obj_fullname; /* full name of object */ + uintptr_t obj_base; /* base address of object */ + uintptr_t obj_size; /* in memory size of object in bytes */ +} mdb_object_t; + extern int mdb_pwalk(const char *, mdb_walk_cb_t, void *, uintptr_t); extern int mdb_walk(const char *, mdb_walk_cb_t, void *); @@ -211,6 +216,11 @@ extern int mdb_lookup_by_name(const char *, GElf_Sym *); extern int mdb_lookup_by_obj(const char *, const char *, GElf_Sym *); extern int mdb_lookup_by_addr(uintptr_t, uint_t, char *, size_t, GElf_Sym *); +typedef uintptr_t mdb_tid_t; +typedef uint64_t mdb_reg_t; + +extern int mdb_getareg(mdb_tid_t, const char *, mdb_reg_t *); + #define MDB_OPT_SETBITS 1 /* Set specified flag bits */ #define MDB_OPT_CLRBITS 2 /* Clear specified flag bits */ #define MDB_OPT_STR 3 /* const char * argument */ @@ -272,6 +282,9 @@ extern void mdb_set_pipe(const mdb_pipe_t *); extern ssize_t mdb_get_xdata(const char *, void *, size_t); +typedef int (*mdb_object_cb_t)(mdb_object_t *, void *); +extern int mdb_object_iter(mdb_object_cb_t, void *); + #define MDB_STATE_IDLE 0 /* Target is idle (not running yet) */ #define MDB_STATE_RUNNING 1 /* Target is currently executing */ #define MDB_STATE_STOPPED 2 /* Target is stopped */ diff --git a/usr/src/cmd/mdb/common/mdb/mdb_module_load.c b/usr/src/cmd/mdb/common/mdb/mdb_module_load.c index 83ba970eca..324b43e461 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_module_load.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_module_load.c @@ -18,9 +18,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include <sys/param.h> @@ -148,10 +148,10 @@ typedef struct mdb_modload_data { /*ARGSUSED*/ static int -module_load(void *fp, const mdb_map_t *map, const char *name) +module_load(void *fp, const mdb_map_t *map, const char *fullname) { mdb_modload_data_t *mld = fp; - name = strbasename(name); + const char *name = strbasename(fullname); if (mdb_module_load(name, mld->mld_mode) == 0 && mdb.m_term != NULL) { if (mld->mld_first == TRUE) { @@ -162,6 +162,23 @@ module_load(void *fp, const mdb_map_t *map, const char *name) mdb_iob_flush(mdb.m_out); } + if (strstr(fullname, "/libc/") != NULL) { + /* + * A bit of a kludge: because we manage alternately capable + * libc instances by mounting the appropriately capable + * instance over /lib/libc.so.1, we may find that our object + * list does not contain libc.so.1, but rather one of its + * hwcap variants. Unfortunately, there is not a way of + * getting from this shared object to the object that it is + * effectively interposing on -- which means that without + * special processing, we will not load any libc.so dmod. So + * if we see that we have a shared object coming out of the + * "libc" directory, we assume that we have a "libc-like" + * object, and explicitly load the "libc.so" dmod. + */ + return (module_load(fp, map, "libc.so.1")); + } + return (0); } diff --git a/usr/src/cmd/mdb/common/mdb/mdb_param.h b/usr/src/cmd/mdb/common/mdb/mdb_param.h index 330011ea1b..b5e64d34b0 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_param.h +++ b/usr/src/cmd/mdb/common/mdb/mdb_param.h @@ -2,9 +2,8 @@ * 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. + * 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 * or http://www.opensolaris.org/os/licensing. @@ -20,15 +19,12 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _MDB_PARAM_H #define _MDB_PARAM_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -112,6 +108,8 @@ extern uintptr_t _mdb_ks_argsbase; extern unsigned long _mdb_ks_msg_bsize; extern unsigned long _mdb_ks_defaultstksz; extern int _mdb_ks_ncpu; +extern int _mdb_ks_ncpu_log2; +extern int _mdb_ks_ncpu_p2; /* * Now derive all the macros using the global variables defined in @@ -134,6 +132,8 @@ extern int _mdb_ks_ncpu; #define MSG_BSIZE _mdb_ks_msg_bsize #define DEFAULTSTKSZ _mdb_ks_defaultstksz #define NCPU _mdb_ks_ncpu +#define NCPU_LOG2 _mdb_ks_ncpu_log2 +#define NCPU_P2 _mdb_ks_ncpu_p2 #define _STRING_H /* Do not re-include <string.h> */ diff --git a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern index 256f4bed86..ff6292e606 100644 --- a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern +++ b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern @@ -88,6 +88,7 @@ SYMBOL_SCOPE { mdb_gelf_destroy { FLAGS = EXTERN }; mdb_gelf_sect_by_name { FLAGS = EXTERN }; mdb_gelf_sect_load { FLAGS = EXTERN }; + mdb_getareg { FLAGS = EXTERN }; mdb_get_dot { FLAGS = EXTERN }; mdb_get_lbolt { FLAGS = EXTERN }; mdb_get_pipe { FLAGS = EXTERN }; @@ -113,6 +114,7 @@ SYMBOL_SCOPE { mdb_memio_create { FLAGS = EXTERN }; mdb_name_to_major { FLAGS = EXTERN }; mdb_nhconvert { FLAGS = EXTERN }; + mdb_object_iter { FLAGS = EXTERN }; mdb_one_bit { FLAGS = EXTERN }; mdb_page2pfn { FLAGS = EXTERN }; mdb_page_lookup { FLAGS = EXTERN }; diff --git a/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c b/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c index 10eb6c80c9..265322e3f5 100644 --- a/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c +++ b/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c @@ -20,8 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -435,10 +434,12 @@ dtracemdb_bufsnap(dtrace_buffer_t *which, dtrace_bufdesc_t *desc) } /* - * This is essentially identical to its cousin in the kernel. + * This is essentially identical to its cousin in the kernel -- with the + * notable exception that we automatically set DTRACEOPT_GRABANON if this + * state is an anonymous enabling. */ static dof_hdr_t * -dtracemdb_dof_create(dtrace_state_t *state) +dtracemdb_dof_create(dtrace_state_t *state, int isanon) { dof_hdr_t *dof; dof_sec_t *sec; @@ -490,6 +491,9 @@ dtracemdb_dof_create(dtrace_state_t *state) opt[i].dofo_value = state->dts_options[i]; } + if (isanon) + opt[DTRACEOPT_GRABANON].dofo_value = 1; + return (dof); } @@ -621,6 +625,7 @@ typedef struct dtracemdb_data { char *dtmd_symstr; char *dtmd_modstr; uintptr_t dtmd_addr; + int dtmd_isanon; } dtracemdb_data_t; static int @@ -645,7 +650,7 @@ dtracemdb_ioctl(void *varg, int cmd, void *arg) case DTRACEIOC_DOFGET: { dof_hdr_t *hdr = arg, *dof; - dof = dtracemdb_dof_create(state); + dof = dtracemdb_dof_create(state, data->dtmd_isanon); bcopy(dof, hdr, MIN(hdr->dofh_loadsz, dof->dofh_loadsz)); mdb_free(dof, dof->dofh_loadsz); @@ -974,6 +979,7 @@ dtrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) dtrace_optval_t val; dtracemdb_data_t md; int rval = DCMD_ERR; + dtrace_anon_t anon; if (!(flags & DCMD_ADDRSPEC)) return (DCMD_USAGE); @@ -991,6 +997,15 @@ dtrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) return (DCMD_ERR); } + if (state.dts_anon != NULL) { + addr = (uintptr_t)state.dts_anon; + + if (mdb_vread(&state, sizeof (state), addr) == -1) { + mdb_warn("couldn't read anonymous state at %p", addr); + return (DCMD_ERR); + } + } + bzero(&md, sizeof (md)); md.dtmd_state = &state; @@ -1001,6 +1016,17 @@ dtrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) return (DCMD_ERR); } + /* + * If this is the anonymous enabling, we need to set a bit indicating + * that DTRACEOPT_GRABANON should be set. + */ + if (mdb_readvar(&anon, "dtrace_anon") == -1) { + mdb_warn("failed to read 'dtrace_anon'"); + return (DCMD_ERR); + } + + md.dtmd_isanon = ((uintptr_t)anon.dta_state == addr); + if (dtrace_go(dtp) != 0) { mdb_warn("failed to initialize dtrace: %s\n", dtrace_errmsg(dtp, dtrace_errno(dtp))); @@ -1750,6 +1776,7 @@ typedef struct dtrace_dynvar_data { uintptr_t dtdvd_hashsize; uintptr_t dtdvd_next; uintptr_t dtdvd_ndx; + uintptr_t dtdvd_sink; } dtrace_dynvar_data_t; int @@ -1759,6 +1786,7 @@ dtrace_dynvar_init(mdb_walk_state_t *wsp) dtrace_dstate_t dstate; dtrace_dynvar_data_t *data; size_t hsize; + GElf_Sym sym; if ((addr = wsp->walk_addr) == NULL) { mdb_warn("dtrace_dynvar walk needs dtrace_dstate_t\n"); @@ -1770,11 +1798,17 @@ dtrace_dynvar_init(mdb_walk_state_t *wsp) return (WALK_ERR); } + if (mdb_lookup_by_name("dtrace_dynhash_sink", &sym) == -1) { + mdb_warn("couldn't find 'dtrace_dynhash_sink'"); + return (WALK_ERR); + } + data = mdb_zalloc(sizeof (dtrace_dynvar_data_t), UM_SLEEP); data->dtdvd_hashsize = dstate.dtds_hashsize; hsize = dstate.dtds_hashsize * sizeof (dtrace_dynhash_t); data->dtdvd_hash = mdb_alloc(hsize, UM_SLEEP); + data->dtdvd_sink = (uintptr_t)sym.st_value; if (mdb_vread(data->dtdvd_hash, hsize, (uintptr_t)dstate.dtds_hash) == -1) { @@ -1785,6 +1819,8 @@ dtrace_dynvar_init(mdb_walk_state_t *wsp) return (WALK_ERR); } + data->dtdvd_next = (uintptr_t)data->dtdvd_hash[0].dtdh_chain; + wsp->walk_data = data; return (WALK_NEXT); } @@ -1798,7 +1834,7 @@ dtrace_dynvar_step(mdb_walk_state_t *wsp) uintptr_t addr; int nkeys; - while ((addr = data->dtdvd_next) == NULL) { + while ((addr = data->dtdvd_next) == data->dtdvd_sink) { if (data->dtdvd_ndx == data->dtdvd_hashsize) return (WALK_DONE); diff --git a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files index c07bd9cb70..cbf2f35b6a 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files +++ b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files @@ -19,8 +19,7 @@ # CDDL HEADER END # # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. +# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. # # @@ -41,6 +40,7 @@ GENUNIX_SRCS = \ devinfo.c \ dist.c \ findstack.c \ + findstack_subr.c \ fm.c \ genunix.c \ group.c \ diff --git a/usr/src/cmd/mdb/common/modules/genunix/devinfo.c b/usr/src/cmd/mdb/common/modules/genunix/devinfo.c index 5d05dbbf9b..513103a682 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/devinfo.c +++ b/usr/src/cmd/mdb/common/modules/genunix/devinfo.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. */ #include <sys/types.h> @@ -1108,8 +1107,8 @@ devinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) DEVI_ATTACHED_CHILDREN, DEVI_ATTACHED_CHILDREN}, { "BRANCH_HELD", DEVI_BRANCH_HELD, DEVI_BRANCH_HELD }, { "NO_BIND", DEVI_NO_BIND, DEVI_NO_BIND }, - { "DEVI_REGISTERED_DEVID", - DEVI_REGISTERED_DEVID, DEVI_REGISTERED_DEVID }, + { "DEVI_CACHED_DEVID", + DEVI_CACHED_DEVID, DEVI_CACHED_DEVID }, { "PHCI_SIGNALS_VHCI", DEVI_PHCI_SIGNALS_VHCI, DEVI_PHCI_SIGNALS_VHCI }, diff --git a/usr/src/cmd/mdb/common/modules/genunix/findstack.c b/usr/src/cmd/mdb/common/modules/genunix/findstack.c index ccb50ae26e..223764eda9 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/findstack.c +++ b/usr/src/cmd/mdb/common/modules/genunix/findstack.c @@ -18,9 +18,9 @@ * * CDDL HEADER END */ + /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ #include <mdb/mdb_modapi.h> @@ -31,130 +31,13 @@ #include <sys/stack.h> #include <sys/thread.h> #include <sys/modctl.h> +#include <assert.h> #include "findstack.h" #include "thread.h" #include "sobj.h" -typedef struct findstack_info { - uintptr_t *fsi_stack; /* place to record frames */ - - uintptr_t fsi_sp; /* stack pointer */ - uintptr_t fsi_pc; /* pc */ - uintptr_t fsi_sobj_ops; /* sobj_ops */ - - uint_t fsi_tstate; /* t_state */ - - uchar_t fsi_depth; /* stack depth */ - uchar_t fsi_failed; /* search failed */ - uchar_t fsi_overflow; /* stack was deeper than max_depth */ - uchar_t fsi_panic; /* thread called panic() */ - - uchar_t fsi_max_depth; /* stack frames available */ -} findstack_info_t; -#define FSI_FAIL_BADTHREAD 1 -#define FSI_FAIL_NOTINMEMORY 2 -#define FSI_FAIL_THREADCORRUPT 3 -#define FSI_FAIL_STACKNOTFOUND 4 - -#ifndef STACK_BIAS -#define STACK_BIAS 0 -#endif - -#define fs_dprintf(x) \ - if (findstack_debug_on) { \ - mdb_printf("findstack debug: "); \ - /*CSTYLED*/ \ - mdb_printf x ; \ - } - -static int findstack_debug_on = 0; - -#if defined(__i386) || defined(__amd64) -struct rwindow { - uintptr_t rw_fp; - uintptr_t rw_rtn; -}; -#endif - -#define TOO_BIG_FOR_A_STACK (1024 * 1024) - -#define KTOU(p) ((p) - kbase + ubase) -#define UTOK(p) ((p) - ubase + kbase) - -#define CRAWL_FOUNDALL (-1) - -/* - * Given a stack pointer, try to crawl down it to the bottom. - * "frame" is a VA in MDB's address space. - * - * Returns the number of frames successfully crawled down, or - * CRAWL_FOUNDALL if it got to the bottom of the stack. - */ -static int -crawl(uintptr_t frame, uintptr_t kbase, uintptr_t ktop, uintptr_t ubase, - int kill_fp, findstack_info_t *fsip) -{ - int levels = 0; - - fsip->fsi_depth = 0; - fsip->fsi_overflow = 0; - - fs_dprintf(("<0> frame = %p, kbase = %p, ktop = %p, ubase = %p\n", - frame, kbase, ktop, ubase)); - for (;;) { - uintptr_t fp; - long *fpp = (long *)&((struct rwindow *)frame)->rw_fp; - - fs_dprintf(("<1> fpp = %p, frame = %p\n", fpp, frame)); - - if ((frame & (STACK_ALIGN - 1)) != 0) - break; - - fp = ((struct rwindow *)frame)->rw_fp + STACK_BIAS; - if (fsip->fsi_depth < fsip->fsi_max_depth) - fsip->fsi_stack[fsip->fsi_depth++] = - ((struct rwindow *)frame)->rw_rtn; - else - fsip->fsi_overflow = 1; - - fs_dprintf(("<2> fp = %p\n", fp)); - - if (fp == ktop) - return (CRAWL_FOUNDALL); - fs_dprintf(("<3> not at base\n")); - -#if defined(__i386) || defined(__amd64) - if (ktop - fp == sizeof (struct rwindow)) { - fs_dprintf(("<4> found base\n")); - return (CRAWL_FOUNDALL); - } -#endif - - fs_dprintf(("<5> fp = %p, kbase = %p, ktop - size = %p\n", - fp, kbase, ktop - sizeof (struct rwindow))); - - if (fp < kbase || fp >= (ktop - sizeof (struct rwindow))) - break; - - frame = KTOU(fp); - fs_dprintf(("<6> frame = %p\n", frame)); - - /* - * NULL out the old %fp so we don't go down this stack - * more than once. - */ - if (kill_fp) { - fs_dprintf(("<7> fpp = %p\n", fpp)); - *fpp = NULL; - } - - fs_dprintf(("<8> levels = %d\n", levels)); - levels++; - } - - return (levels); -} +int findstack_debug_on = 0; /* * "sp" is a kernel VA. @@ -193,164 +76,6 @@ print_stack(uintptr_t sp, uintptr_t pc, uintptr_t addr, return ((err == -1) ? DCMD_ABORT : DCMD_OK); } -/*ARGSUSED*/ -static int -do_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings) -{ - kthread_t thr; - size_t stksz; - uintptr_t ubase, utop; - uintptr_t kbase, ktop; - uintptr_t win, sp; - - fsip->fsi_failed = 0; - fsip->fsi_pc = 0; - fsip->fsi_sp = 0; - fsip->fsi_depth = 0; - fsip->fsi_overflow = 0; - - bzero(&thr, sizeof (thr)); - if (mdb_ctf_vread(&thr, "kthread_t", addr, - MDB_CTF_VREAD_IGNORE_ALL) == -1) { - if (print_warnings) - mdb_warn("couldn't read thread at %p\n", addr); - fsip->fsi_failed = FSI_FAIL_BADTHREAD; - return (DCMD_ERR); - } - - fsip->fsi_sobj_ops = (uintptr_t)thr.t_sobj_ops; - fsip->fsi_tstate = thr.t_state; - fsip->fsi_panic = !!(thr.t_flag & T_PANIC); - - if ((thr.t_schedflag & TS_LOAD) == 0) { - if (print_warnings) - mdb_warn("thread %p isn't in memory\n", addr); - fsip->fsi_failed = FSI_FAIL_NOTINMEMORY; - return (DCMD_ERR); - } - - if (thr.t_stk < thr.t_stkbase) { - if (print_warnings) - mdb_warn( - "stack base or stack top corrupt for thread %p\n", - addr); - fsip->fsi_failed = FSI_FAIL_THREADCORRUPT; - return (DCMD_ERR); - } - - kbase = (uintptr_t)thr.t_stkbase; - ktop = (uintptr_t)thr.t_stk; - stksz = ktop - kbase; - -#ifdef __amd64 - /* - * The stack on amd64 is intentionally misaligned, so ignore the top - * half-frame. See thread_stk_init(). When handling traps, the frame - * is automatically aligned by the hardware, so we only alter ktop if - * needed. - */ - if ((ktop & (STACK_ALIGN - 1)) != 0) - ktop -= STACK_ENTRY_ALIGN; -#endif - - /* - * If the stack size is larger than a meg, assume that it's bogus. - */ - if (stksz > TOO_BIG_FOR_A_STACK) { - if (print_warnings) - mdb_warn("stack size for thread %p is too big to be " - "reasonable\n", addr); - fsip->fsi_failed = FSI_FAIL_THREADCORRUPT; - return (DCMD_ERR); - } - - /* - * This could be (and was) a UM_GC allocation. Unfortunately, - * stksz tends to be very large. As currently implemented, dcmds - * invoked as part of pipelines don't have their UM_GC-allocated - * memory freed until the pipeline completes. With stksz in the - * neighborhood of 20k, the popular ::walk thread |::findstack - * pipeline can easily run memory-constrained debuggers (kmdb) out - * of memory. This can be changed back to a gc-able allocation when - * the debugger is changed to free UM_GC memory more promptly. - */ - ubase = (uintptr_t)mdb_alloc(stksz, UM_SLEEP); - utop = ubase + stksz; - if (mdb_vread((caddr_t)ubase, stksz, kbase) != stksz) { - mdb_free((void *)ubase, stksz); - if (print_warnings) - mdb_warn("couldn't read entire stack for thread %p\n", - addr); - fsip->fsi_failed = FSI_FAIL_THREADCORRUPT; - return (DCMD_ERR); - } - - /* - * Try the saved %sp first, if it looks reasonable. - */ - sp = KTOU((uintptr_t)thr.t_sp + STACK_BIAS); - if (sp >= ubase && sp <= utop) { - if (crawl(sp, kbase, ktop, ubase, 0, fsip) == CRAWL_FOUNDALL) { - fsip->fsi_sp = (uintptr_t)thr.t_sp; -#if !defined(__i386) - fsip->fsi_pc = (uintptr_t)thr.t_pc; -#endif - goto found; - } - } - - /* - * Now walk through the whole stack, starting at the base, - * trying every possible "window". - */ - for (win = ubase; - win + sizeof (struct rwindow) <= utop; - win += sizeof (struct rwindow *)) { - if (crawl(win, kbase, ktop, ubase, 1, fsip) == CRAWL_FOUNDALL) { - fsip->fsi_sp = UTOK(win) - STACK_BIAS; - goto found; - } - } - - /* - * We didn't conclusively find the stack. So we'll take another lap, - * and print out anything that looks possible. - */ - if (print_warnings) - mdb_printf("Possible stack pointers for thread %p:\n", addr); - (void) mdb_vread((caddr_t)ubase, stksz, kbase); - - for (win = ubase; - win + sizeof (struct rwindow) <= utop; - win += sizeof (struct rwindow *)) { - uintptr_t fp = ((struct rwindow *)win)->rw_fp; - int levels; - - if ((levels = crawl(win, kbase, ktop, ubase, 1, fsip)) > 1) { - if (print_warnings) - mdb_printf(" %p (%d)\n", fp, levels); - } else if (levels == CRAWL_FOUNDALL) { - /* - * If this is a live system, the stack could change - * between the two mdb_vread(ubase, utop, kbase)'s, - * and we could have a fully valid stack here. - */ - fsip->fsi_sp = UTOK(win) - STACK_BIAS; - goto found; - } - } - - fsip->fsi_depth = 0; - fsip->fsi_overflow = 0; - fsip->fsi_failed = FSI_FAIL_STACKNOTFOUND; - - mdb_free((void *)ubase, stksz); - return (DCMD_ERR); -found: - mdb_free((void *)ubase, stksz); - return (DCMD_OK); -} - int findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { @@ -362,7 +87,7 @@ findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) bzero(&fsi, sizeof (fsi)); - if ((retval = do_findstack(addr, &fsi, 1)) != DCMD_OK || + if ((retval = stacks_findstack(addr, &fsi, 1)) != DCMD_OK || fsi.fsi_failed) return (retval); @@ -399,6 +124,7 @@ sobj_to_text(uintptr_t addr, char *out, size_t out_sz) } #define SOBJ_ALL 1 + static int text_to_sobj(const char *text, uintptr_t *out) { @@ -406,6 +132,7 @@ text_to_sobj(const char *text, uintptr_t *out) *out = SOBJ_ALL; return (0); } + return (sobj_text_to_ops(text, out)); } @@ -457,11 +184,9 @@ typedef struct stacks_info { size_t si_count; /* total stacks_entry_ts (incl dups) */ size_t si_entries; /* # entries in hash table */ stacks_entry_t **si_hash; /* hash table */ - findstack_info_t si_fsi; /* transient callback state */ } stacks_info_t; - /* global state cached between invocations */ #define STACKS_STATE_CLEAN 0 #define STACKS_STATE_DIRTY 1 @@ -631,6 +356,8 @@ stacks_cleanup(int force) stacks_array_size * sizeof (*stacks_array)); } + stacks_findstack_cleanup(); + stacks_array_size = 0; stacks_state = STACKS_STATE_CLEAN; } @@ -646,7 +373,7 @@ stacks_thread_cb(uintptr_t addr, const void *ignored, void *cbarg) int idx; size_t depth; - if (do_findstack(addr, fsip, 0) != DCMD_OK && + if (stacks_findstack(addr, fsip, 0) != DCMD_OK && fsip->fsi_failed == FSI_FAIL_BADTHREAD) { mdb_warn("couldn't read thread at %p\n", addr); return (WALK_NEXT); @@ -694,19 +421,14 @@ stacks_run_tlist(mdb_pipe_t *tlist, stacks_info_t *si) { size_t idx; size_t found = 0; - kthread_t kt; int ret; for (idx = 0; idx < tlist->pipe_len; idx++) { uintptr_t addr = tlist->pipe_data[idx]; - if (mdb_vread(&kt, sizeof (kt), addr) == -1) { - mdb_warn("unable to read kthread_t at %p", addr); - continue; - } found++; - ret = stacks_thread_cb(addr, &kt, si); + ret = stacks_thread_cb(addr, NULL, si); if (ret == WALK_DONE) break; if (ret != WALK_NEXT) @@ -811,66 +533,36 @@ stacks_has_caller(stacks_entry_t *sep, uintptr_t addr) return (0); } -typedef struct find_module_struct { - struct modctl *mcp; - const char *name; -} find_module_struct_t; - -/*ARGSUSED*/ -int -find_module_cb(uintptr_t addr, const void *modctl_arg, void *cbarg) +static int +stacks_has_module(stacks_entry_t *sep, stacks_module_t *mp) { - find_module_struct_t *sp = cbarg; - char mod_modname[MODMAXNAMELEN + 1]; - const struct modctl *mp = modctl_arg; - - if (!mp->mod_modname) - return (WALK_NEXT); + int idx; - if (mdb_readstr(mod_modname, sizeof (mod_modname), - (uintptr_t)mp->mod_modname) == -1) { - mdb_warn("failed to read mod_modname in \"modctl\" walk"); - return (WALK_ERR); + for (idx = 0; idx < sep->se_depth; idx++) { + if (sep->se_stack[idx] >= mp->sm_text && + sep->se_stack[idx] < mp->sm_text + mp->sm_size) + return (1); } - if (strcmp(sp->name, mod_modname)) - return (WALK_NEXT); - - sp->mcp = mdb_alloc(sizeof (*sp->mcp), UM_SLEEP | UM_GC); - bcopy(mp, sp->mcp, sizeof (*sp->mcp)); - return (WALK_DONE); -} - -static struct modctl * -find_module(const char *name) -{ - find_module_struct_t mptr; - - mptr.name = name; - mptr.mcp = NULL; - - if (mdb_walk("modctl", find_module_cb, &mptr) != 0) - mdb_warn("cannot walk \"modctl\""); - return (mptr.mcp); + return (0); } static int -stacks_has_module(stacks_entry_t *sep, struct modctl *mp) +stacks_module_find(const char *name, stacks_module_t *mp) { - int idx; + (void) strncpy(mp->sm_name, name, sizeof (mp->sm_name)); - if (mp == NULL) - return (0); + if (stacks_module(mp) != 0) + return (-1); + + if (mp->sm_size == 0) { + mdb_warn("stacks: module \"%s\" is unknown\n", name); + return (-1); + } - for (idx = 0; idx < sep->se_depth; idx++) - if (sep->se_stack[idx] >= (uintptr_t)mp->mod_text && - sep->se_stack[idx] < - ((uintptr_t)mp->mod_text + mp->mod_text_size)) - return (1); return (0); } - static int uintptrcomp(const void *lp, const void *rp) { @@ -884,96 +576,6 @@ uintptrcomp(const void *lp, const void *rp) } /*ARGSUSED*/ -static void -print_sobj_help(int type, const char *name, const char *ops_name, void *ign) -{ - mdb_printf(" %s", name); -} - -/*ARGSUSED*/ -static void -print_tstate_help(uint_t state, const char *name, void *ignored) -{ - mdb_printf(" %s", name); -} - -void -stacks_help(void) -{ - mdb_printf( -"::stacks processes all of the thread stacks on the system, grouping\n" -"together threads which have the same:\n" -"\n" -" * Thread state,\n" -" * Sync object type, and\n" -" * PCs in their stack trace.\n" -"\n" -"The default output (no address or options) is just a dump of the thread\n" -"groups in the system. For a view of active threads, use \"::stacks -i\",\n" -"which filters out FREE threads (interrupt threads which are currently\n" -"inactive) and threads sleeping on a CV. (Note that those threads may still\n" -"be noteworthy; this is just for a first glance.) More general filtering\n" -"options are described below, in the \"FILTERS\" section.\n" -"\n" -"::stacks can be used in a pipeline. The input to ::stacks is one or more\n" -"thread pointers. For example, to get a summary of threads in a process,\n" -"you can do:\n" -"\n" -" %<b>procp%</b>::walk thread | ::stacks\n" -"\n" -"When output into a pipe, ::stacks prints all of the threads input,\n" -"filtered by the given filtering options. This means that multiple\n" -"::stacks invocations can be piped together to achieve more complicated\n" -"filters. For example, to get threads which have both 'fop_read' and\n" -"'cv_wait_sig_swap' in their stack trace, you could do:\n" -"\n" -" ::stacks -c fop_read | ::stacks -c cv_wait_sig_swap_core\n" -"\n" -"To get the full list of threads in each group, use the '-a' flag:\n" -"\n" -" ::stacks -a\n" -"\n"); - mdb_dec_indent(2); - mdb_printf("%<b>OPTIONS%</b>\n"); - mdb_inc_indent(2); - mdb_printf("%s", -" -a Print all of the grouped threads, instead of just a count.\n" -" -f Force a re-run of the thread stack gathering.\n" -" -v Be verbose about thread stack gathering.\n" -"\n"); - mdb_dec_indent(2); - mdb_printf("%<b>FILTERS%</b>\n"); - mdb_inc_indent(2); - mdb_printf("%s", -" -i Show active threads; equivalent to '-S CV -T FREE'.\n" -" -c func[+offset]\n" -" Only print threads whose stacks contain func/func+offset.\n" -" -C func[+offset]\n" -" Only print threads whose stacks do not contain func/func+offset.\n" -" -m module\n" -" Only print threads whose stacks contain functions from module.\n" -" -M module\n" -" Only print threads whose stacks do not contain functions from\n" -" module.\n" -" -s {type | ALL}\n" -" Only print threads which are on a 'type' synchronization object\n" -" (SOBJ).\n" -" -S {type | ALL}\n" -" Only print threads which are not on a 'type' SOBJ.\n" -" -t tstate\n" -" Only print threads which are in thread state 'tstate'.\n" -" -T tstate\n" -" Only print threads which are not in thread state 'tstate'.\n" -"\n"); - mdb_printf(" SOBJ types:"); - sobj_type_walk(print_sobj_help, NULL); - mdb_printf("\n"); - mdb_printf("Thread states:"); - thread_walk_states(print_tstate_help, NULL); - mdb_printf(" panic\n"); -} - -/*ARGSUSED*/ int stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { @@ -986,7 +588,7 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) uintptr_t caller = 0, excl_caller = 0; const char *module_str = NULL; const char *excl_module_str = NULL; - struct modctl *module = NULL, *excl_module = NULL; + stacks_module_t module, excl_module; const char *sobj = NULL; const char *excl_sobj = NULL; uintptr_t sobj_ops = 0, excl_sobj_ops = 0; @@ -1015,6 +617,9 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) mdb_pipe_t p; + bzero(&module, sizeof (module)); + bzero(&excl_module, sizeof (excl_module)); + if (mdb_getopts(argc, argv, 'a', MDB_OPT_SETBITS, TRUE, &all, 'f', MDB_OPT_SETBITS, TRUE, &force, @@ -1063,24 +668,17 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) } mdb_set_dot(addr); - if (module_str != NULL && - (module = find_module(module_str)) == NULL) { - mdb_warn("stacks: module \"%s\" is unknown", module_str); + if (module_str != NULL && stacks_module_find(module_str, &module) != 0) return (DCMD_ABORT); - } if (excl_module_str != NULL && - (excl_module = find_module(excl_module_str)) == NULL) { - mdb_warn("stacks: module \"%s\" is unknown", excl_module_str); + stacks_module_find(excl_module_str, &excl_module) != 0) return (DCMD_ABORT); - } - if (sobj != NULL && - text_to_sobj(sobj, &sobj_ops) != 0) + if (sobj != NULL && text_to_sobj(sobj, &sobj_ops) != 0) return (DCMD_USAGE); - if (excl_sobj != NULL && - text_to_sobj(excl_sobj, &excl_sobj_ops) != 0) + if (excl_sobj != NULL && text_to_sobj(excl_sobj, &excl_sobj_ops) != 0) return (DCMD_USAGE); if (sobj_ops != 0 && excl_sobj_ops != 0) { @@ -1088,8 +686,7 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) return (DCMD_USAGE); } - if (tstate_str != NULL && - text_to_tstate(tstate_str, &tstate) != 0) + if (tstate_str != NULL && text_to_tstate(tstate_str, &tstate) != 0) return (DCMD_USAGE); if (excl_tstate_str != NULL && @@ -1190,11 +787,15 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) if (caller != 0 && !stacks_has_caller(sep, caller)) continue; + if (excl_caller != 0 && stacks_has_caller(sep, excl_caller)) continue; - if (module != 0 && !stacks_has_module(sep, module)) + + if (module.sm_size != 0 && !stacks_has_module(sep, &module)) continue; - if (excl_module != 0 && stacks_has_module(sep, excl_module)) + + if (excl_module.sm_size != 0 && + stacks_has_module(sep, &excl_module)) continue; if (tstate != -1U) { @@ -1256,10 +857,10 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) sobj, sizeof (sobj)); if (cur == sep) - mdb_printf("%?p %-8s %-?s %8d\n", + mdb_printf("%-?p %-8s %-?s %8d\n", cur->se_thread, state, sobj, count); else - mdb_printf("%?p %-8s %-?s %8s\n", + mdb_printf("%-?p %-8s %-?s %8s\n", cur->se_thread, state, sobj, "-"); cur = only_matching ? cur->se_next : cur->se_dup; diff --git a/usr/src/cmd/mdb/common/modules/genunix/findstack.h b/usr/src/cmd/mdb/common/modules/genunix/findstack.h index 304735c931..d81ae1ab6f 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/findstack.h +++ b/usr/src/cmd/mdb/common/modules/genunix/findstack.h @@ -18,27 +18,74 @@ * * CDDL HEADER END */ + /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _MDB_FINDSTACK_H #define _MDB_FINDSTACK_H #include <mdb/mdb_modapi.h> +#include <sys/param.h> #ifdef __cplusplus extern "C" { #endif +typedef struct findstack_info { + uintptr_t *fsi_stack; /* place to record frames */ + uintptr_t fsi_sp; /* stack pointer */ + uintptr_t fsi_pc; /* pc */ + uintptr_t fsi_sobj_ops; /* sobj_ops */ + uint_t fsi_tstate; /* t_state */ + uchar_t fsi_depth; /* stack depth */ + uchar_t fsi_failed; /* search failed */ + uchar_t fsi_overflow; /* stack was deeper than max_depth */ + uchar_t fsi_panic; /* thread called panic() */ + uchar_t fsi_max_depth; /* stack frames available */ +} findstack_info_t; + +#define FSI_FAIL_BADTHREAD 1 +#define FSI_FAIL_NOTINMEMORY 2 +#define FSI_FAIL_THREADCORRUPT 3 +#define FSI_FAIL_STACKNOTFOUND 4 + +typedef struct stacks_module { + char sm_name[MAXPATHLEN]; /* name of module */ + uintptr_t sm_text; /* base address of text in module */ + size_t sm_size; /* size of text in module */ +} stacks_module_t; + extern int findstack(uintptr_t, uint_t, int, const mdb_arg_t *); extern int findstack_debug(uintptr_t, uint_t, int, const mdb_arg_t *); +/* + * The following routines are implemented in findstack.c, shared across both + * genunix and libc. + */ extern int stacks(uintptr_t, uint_t, int, const mdb_arg_t *); -extern void stacks_help(void); extern void stacks_cleanup(int); +/* + * The following routines are specific to their context (kernel vs. user-land) + * and are therefore implemented in findstack_subr.c (of which each of genunix + * and libc have their own copy). + */ +extern void stacks_help(void); +extern int stacks_findstack(uintptr_t, findstack_info_t *, uint_t); +extern void stacks_findstack_cleanup(); +extern int stacks_module(stacks_module_t *); + +extern int findstack_debug_on; + +#define fs_dprintf(x) \ + if (findstack_debug_on) { \ + mdb_printf("findstack debug: "); \ + /*CSTYLED*/ \ + mdb_printf x ; \ + } + #ifdef __cplusplus } #endif diff --git a/usr/src/cmd/mdb/common/modules/genunix/findstack_subr.c b/usr/src/cmd/mdb/common/modules/genunix/findstack_subr.c new file mode 100644 index 0000000000..aa68d0236a --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/genunix/findstack_subr.c @@ -0,0 +1,414 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include <mdb/mdb_modapi.h> +#include <mdb/mdb_ctf.h> + +#include <sys/types.h> +#include <sys/regset.h> +#include <sys/stack.h> +#include <sys/thread.h> +#include <sys/modctl.h> + +#include "findstack.h" +#include "thread.h" +#include "sobj.h" + +#define TOO_BIG_FOR_A_STACK (1024 * 1024) + +#define KTOU(p) ((p) - kbase + ubase) +#define UTOK(p) ((p) - ubase + kbase) + +#define CRAWL_FOUNDALL (-1) + +#if defined(__i386) || defined(__amd64) +struct rwindow { + uintptr_t rw_fp; + uintptr_t rw_rtn; +}; +#endif + +#ifndef STACK_BIAS +#define STACK_BIAS 0 +#endif + +/* + * Given a stack pointer, try to crawl down it to the bottom. + * "frame" is a VA in MDB's address space. + * + * Returns the number of frames successfully crawled down, or + * CRAWL_FOUNDALL if it got to the bottom of the stack. + */ +static int +crawl(uintptr_t frame, uintptr_t kbase, uintptr_t ktop, uintptr_t ubase, + int kill_fp, findstack_info_t *fsip) +{ + int levels = 0; + + fsip->fsi_depth = 0; + fsip->fsi_overflow = 0; + + fs_dprintf(("<0> frame = %p, kbase = %p, ktop = %p, ubase = %p\n", + frame, kbase, ktop, ubase)); + for (;;) { + uintptr_t fp; + long *fpp = (long *)&((struct rwindow *)frame)->rw_fp; + + fs_dprintf(("<1> fpp = %p, frame = %p\n", fpp, frame)); + + if ((frame & (STACK_ALIGN - 1)) != 0) + break; + + fp = ((struct rwindow *)frame)->rw_fp + STACK_BIAS; + if (fsip->fsi_depth < fsip->fsi_max_depth) + fsip->fsi_stack[fsip->fsi_depth++] = + ((struct rwindow *)frame)->rw_rtn; + else + fsip->fsi_overflow = 1; + + fs_dprintf(("<2> fp = %p\n", fp)); + + if (fp == ktop) + return (CRAWL_FOUNDALL); + fs_dprintf(("<3> not at base\n")); + +#if defined(__i386) || defined(__amd64) + if (ktop - fp == sizeof (struct rwindow)) { + fs_dprintf(("<4> found base\n")); + return (CRAWL_FOUNDALL); + } +#endif + + fs_dprintf(("<5> fp = %p, kbase = %p, ktop - size = %p\n", + fp, kbase, ktop - sizeof (struct rwindow))); + + if (fp < kbase || fp >= (ktop - sizeof (struct rwindow))) + break; + + frame = KTOU(fp); + fs_dprintf(("<6> frame = %p\n", frame)); + + /* + * NULL out the old %fp so we don't go down this stack + * more than once. + */ + if (kill_fp) { + fs_dprintf(("<7> fpp = %p\n", fpp)); + *fpp = NULL; + } + + fs_dprintf(("<8> levels = %d\n", levels)); + levels++; + } + + return (levels); +} + +/*ARGSUSED*/ +int +stacks_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings) +{ + kthread_t thr; + size_t stksz; + uintptr_t ubase, utop; + uintptr_t kbase, ktop; + uintptr_t win, sp; + + fsip->fsi_failed = 0; + fsip->fsi_pc = 0; + fsip->fsi_sp = 0; + fsip->fsi_depth = 0; + fsip->fsi_overflow = 0; + + bzero(&thr, sizeof (thr)); + if (mdb_ctf_vread(&thr, "kthread_t", addr, + MDB_CTF_VREAD_IGNORE_ALL) == -1) { + if (print_warnings) + mdb_warn("couldn't read thread at %p\n", addr); + fsip->fsi_failed = FSI_FAIL_BADTHREAD; + return (DCMD_ERR); + } + + fsip->fsi_sobj_ops = (uintptr_t)thr.t_sobj_ops; + fsip->fsi_tstate = thr.t_state; + fsip->fsi_panic = !!(thr.t_flag & T_PANIC); + + if ((thr.t_schedflag & TS_LOAD) == 0) { + if (print_warnings) + mdb_warn("thread %p isn't in memory\n", addr); + fsip->fsi_failed = FSI_FAIL_NOTINMEMORY; + return (DCMD_ERR); + } + + if (thr.t_stk < thr.t_stkbase) { + if (print_warnings) + mdb_warn( + "stack base or stack top corrupt for thread %p\n", + addr); + fsip->fsi_failed = FSI_FAIL_THREADCORRUPT; + return (DCMD_ERR); + } + + kbase = (uintptr_t)thr.t_stkbase; + ktop = (uintptr_t)thr.t_stk; + stksz = ktop - kbase; + +#ifdef __amd64 + /* + * The stack on amd64 is intentionally misaligned, so ignore the top + * half-frame. See thread_stk_init(). When handling traps, the frame + * is automatically aligned by the hardware, so we only alter ktop if + * needed. + */ + if ((ktop & (STACK_ALIGN - 1)) != 0) + ktop -= STACK_ENTRY_ALIGN; +#endif + + /* + * If the stack size is larger than a meg, assume that it's bogus. + */ + if (stksz > TOO_BIG_FOR_A_STACK) { + if (print_warnings) + mdb_warn("stack size for thread %p is too big to be " + "reasonable\n", addr); + fsip->fsi_failed = FSI_FAIL_THREADCORRUPT; + return (DCMD_ERR); + } + + /* + * This could be (and was) a UM_GC allocation. Unfortunately, + * stksz tends to be very large. As currently implemented, dcmds + * invoked as part of pipelines don't have their UM_GC-allocated + * memory freed until the pipeline completes. With stksz in the + * neighborhood of 20k, the popular ::walk thread |::findstack + * pipeline can easily run memory-constrained debuggers (kmdb) out + * of memory. This can be changed back to a gc-able allocation when + * the debugger is changed to free UM_GC memory more promptly. + */ + ubase = (uintptr_t)mdb_alloc(stksz, UM_SLEEP); + utop = ubase + stksz; + if (mdb_vread((caddr_t)ubase, stksz, kbase) != stksz) { + mdb_free((void *)ubase, stksz); + if (print_warnings) + mdb_warn("couldn't read entire stack for thread %p\n", + addr); + fsip->fsi_failed = FSI_FAIL_THREADCORRUPT; + return (DCMD_ERR); + } + + /* + * Try the saved %sp first, if it looks reasonable. + */ + sp = KTOU((uintptr_t)thr.t_sp + STACK_BIAS); + if (sp >= ubase && sp <= utop) { + if (crawl(sp, kbase, ktop, ubase, 0, fsip) == CRAWL_FOUNDALL) { + fsip->fsi_sp = (uintptr_t)thr.t_sp; +#if !defined(__i386) + fsip->fsi_pc = (uintptr_t)thr.t_pc; +#endif + goto found; + } + } + + /* + * Now walk through the whole stack, starting at the base, + * trying every possible "window". + */ + for (win = ubase; + win + sizeof (struct rwindow) <= utop; + win += sizeof (struct rwindow *)) { + if (crawl(win, kbase, ktop, ubase, 1, fsip) == CRAWL_FOUNDALL) { + fsip->fsi_sp = UTOK(win) - STACK_BIAS; + goto found; + } + } + + /* + * We didn't conclusively find the stack. So we'll take another lap, + * and print out anything that looks possible. + */ + if (print_warnings) + mdb_printf("Possible stack pointers for thread %p:\n", addr); + (void) mdb_vread((caddr_t)ubase, stksz, kbase); + + for (win = ubase; + win + sizeof (struct rwindow) <= utop; + win += sizeof (struct rwindow *)) { + uintptr_t fp = ((struct rwindow *)win)->rw_fp; + int levels; + + if ((levels = crawl(win, kbase, ktop, ubase, 1, fsip)) > 1) { + if (print_warnings) + mdb_printf(" %p (%d)\n", fp, levels); + } else if (levels == CRAWL_FOUNDALL) { + /* + * If this is a live system, the stack could change + * between the two mdb_vread(ubase, utop, kbase)'s, + * and we could have a fully valid stack here. + */ + fsip->fsi_sp = UTOK(win) - STACK_BIAS; + goto found; + } + } + + fsip->fsi_depth = 0; + fsip->fsi_overflow = 0; + fsip->fsi_failed = FSI_FAIL_STACKNOTFOUND; + + mdb_free((void *)ubase, stksz); + return (DCMD_ERR); +found: + mdb_free((void *)ubase, stksz); + return (DCMD_OK); +} + +void +stacks_findstack_cleanup() +{} + +/*ARGSUSED*/ +int +stacks_module_cb(uintptr_t addr, const modctl_t *mp, stacks_module_t *smp) +{ + char mod_modname[MODMAXNAMELEN + 1]; + + if (!mp->mod_modname) + return (WALK_NEXT); + + if (mdb_readstr(mod_modname, sizeof (mod_modname), + (uintptr_t)mp->mod_modname) == -1) { + mdb_warn("failed to read mod_modname in \"modctl\" walk"); + return (WALK_ERR); + } + + if (strcmp(smp->sm_name, mod_modname)) + return (WALK_NEXT); + + smp->sm_text = (uintptr_t)mp->mod_text; + smp->sm_size = mp->mod_text_size; + + return (WALK_DONE); +} + +int +stacks_module(stacks_module_t *smp) +{ + if (mdb_walk("modctl", (mdb_walk_cb_t)stacks_module_cb, smp) != 0) { + mdb_warn("cannot walk \"modctl\""); + return (-1); + } + + return (0); +} + +/*ARGSUSED*/ +static void +print_sobj_help(int type, const char *name, const char *ops_name, void *ign) +{ + mdb_printf(" %s", name); +} + +/*ARGSUSED*/ +static void +print_tstate_help(uint_t state, const char *name, void *ignored) +{ + mdb_printf(" %s", name); +} + +void +stacks_help(void) +{ + mdb_printf( +"::stacks processes all of the thread stacks on the system, grouping\n" +"together threads which have the same:\n" +"\n" +" * Thread state,\n" +" * Sync object type, and\n" +" * PCs in their stack trace.\n" +"\n" +"The default output (no address or options) is just a dump of the thread\n" +"groups in the system. For a view of active threads, use \"::stacks -i\",\n" +"which filters out FREE threads (interrupt threads which are currently\n" +"inactive) and threads sleeping on a CV. (Note that those threads may still\n" +"be noteworthy; this is just for a first glance.) More general filtering\n" +"options are described below, in the \"FILTERS\" section.\n" +"\n" +"::stacks can be used in a pipeline. The input to ::stacks is one or more\n" +"thread pointers. For example, to get a summary of threads in a process,\n" +"you can do:\n" +"\n" +" %<b>procp%</b>::walk thread | ::stacks\n" +"\n" +"When output into a pipe, ::stacks prints all of the threads input,\n" +"filtered by the given filtering options. This means that multiple\n" +"::stacks invocations can be piped together to achieve more complicated\n" +"filters. For example, to get threads which have both 'fop_read' and\n" +"'cv_wait_sig_swap' in their stack trace, you could do:\n" +"\n" +" ::stacks -c fop_read | ::stacks -c cv_wait_sig_swap_core\n" +"\n" +"To get the full list of threads in each group, use the '-a' flag:\n" +"\n" +" ::stacks -a\n" +"\n"); + mdb_dec_indent(2); + mdb_printf("%<b>OPTIONS%</b>\n"); + mdb_inc_indent(2); + mdb_printf("%s", +" -a Print all of the grouped threads, instead of just a count.\n" +" -f Force a re-run of the thread stack gathering.\n" +" -v Be verbose about thread stack gathering.\n" +"\n"); + mdb_dec_indent(2); + mdb_printf("%<b>FILTERS%</b>\n"); + mdb_inc_indent(2); + mdb_printf("%s", +" -i Show active threads; equivalent to '-S CV -T FREE'.\n" +" -c func[+offset]\n" +" Only print threads whose stacks contain func/func+offset.\n" +" -C func[+offset]\n" +" Only print threads whose stacks do not contain func/func+offset.\n" +" -m module\n" +" Only print threads whose stacks contain functions from module.\n" +" -M module\n" +" Only print threads whose stacks do not contain functions from\n" +" module.\n" +" -s {type | ALL}\n" +" Only print threads which are on a 'type' synchronization object\n" +" (SOBJ).\n" +" -S {type | ALL}\n" +" Only print threads which are not on a 'type' SOBJ.\n" +" -t tstate\n" +" Only print threads which are in thread state 'tstate'.\n" +" -T tstate\n" +" Only print threads which are not in thread state 'tstate'.\n" +"\n"); + mdb_printf(" SOBJ types:"); + sobj_type_walk(print_sobj_help, NULL); + mdb_printf("\n"); + mdb_printf("Thread states:"); + thread_walk_states(print_tstate_help, NULL); + mdb_printf(" panic\n"); +} diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c index 968f0284c4..3004495d6b 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c +++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ #include <mdb/mdb_param.h> @@ -3798,17 +3797,28 @@ panicinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) static int time(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { + uint_t opt_dec = FALSE; uint_t opt_lbolt = FALSE; + uint_t opt_hex = FALSE; + const char *fmt; + hrtime_t result; - if (mdb_getopts(argc, argv, 'l', MDB_OPT_SETBITS, TRUE, &opt_lbolt, + if (mdb_getopts(argc, argv, + 'd', MDB_OPT_SETBITS, TRUE, &opt_dec, + 'l', MDB_OPT_SETBITS, TRUE, &opt_lbolt, + 'x', MDB_OPT_SETBITS, TRUE, &opt_hex, NULL) != argc) return (DCMD_USAGE); - if (opt_lbolt) - mdb_printf("%ld\n", mdb_get_lbolt()); - else - mdb_printf("%lld\n", mdb_gethrtime()); + if (opt_dec && opt_hex) + return (DCMD_USAGE); + + result = opt_lbolt ? mdb_get_lbolt() : mdb_gethrtime(); + fmt = + opt_hex ? "0x%llx\n" : + opt_dec ? "0t%lld\n" : "%#llr\n"; + mdb_printf(fmt, result); return (DCMD_OK); } @@ -3821,7 +3831,9 @@ time_help(void) "time if inspecting one; or the running hires time if we're \n" "looking at a live system.\n\n" "Switches:\n" - " -l prints the number of clock ticks since system boot\n"); + " -d report times in decimal\n" + " -l prints the number of clock ticks since system boot\n" + " -x report times in hexadecimal\n"); } static const mdb_dcmd_t dcmds[] = { @@ -3862,7 +3874,7 @@ static const mdb_dcmd_t dcmds[] = { "print sysevent subclass list", sysevent_subclass_list}, { "system", NULL, "print contents of /etc/system file", sysfile }, { "task", NULL, "display kernel task(s)", task }, - { "time", "[-l]", "display system time", time, time_help }, + { "time", "[-dlx]", "display system time", time, time_help }, { "vnode2path", ":[-F]", "vnode address to pathname", vnode2path }, { "whereopen", ":", "given a vnode, dumps procs which have it open", whereopen }, @@ -4159,7 +4171,7 @@ static const mdb_dcmd_t dcmds[] = { pfiles_help }, /* from zone.c */ - { "zone", "?", "display kernel zone(s)", zoneprt }, + { "zone", "?[-r [-v]]", "display kernel zone(s)", zoneprt }, { "zsd", ":[-v] [zsd_key]", "display zone-specific-data entries for " "selected zones", zsd }, @@ -4397,14 +4409,36 @@ static const mdb_walker_t walkers[] = { /* from memory.c */ { "allpages", "walk all pages, including free pages", allpages_walk_init, allpages_walk_step, allpages_walk_fini }, - { "anon", "given an amp, list of anon structures", - anon_walk_init, anon_walk_step, anon_walk_fini }, + { "anon", "given an amp, list allocated anon structures", + anon_walk_init, anon_walk_step, anon_walk_fini, + ANON_WALK_ALLOC }, + { "anon_all", "given an amp, list contents of all anon slots", + anon_walk_init, anon_walk_step, anon_walk_fini, + ANON_WALK_ALL }, { "memlist", "walk specified memlist", NULL, memlist_walk_step, NULL }, { "page", "walk all pages, or those from the specified vnode", page_walk_init, page_walk_step, page_walk_fini }, { "seg", "given an as, list of segments", seg_walk_init, avl_walk_step, avl_walk_fini }, + { "segvn_anon", + "given a struct segvn_data, list allocated anon structures", + segvn_anon_walk_init, anon_walk_step, anon_walk_fini, + ANON_WALK_ALLOC }, + { "segvn_anon_all", + "given a struct segvn_data, list contents of all anon slots", + segvn_anon_walk_init, anon_walk_step, anon_walk_fini, + ANON_WALK_ALL }, + { "segvn_pages", + "given a struct segvn_data, list resident pages in " + "offset order", + segvn_pages_walk_init, segvn_pages_walk_step, + segvn_pages_walk_fini, SEGVN_PAGES_RESIDENT }, + { "segvn_pages_all", + "for each offset in a struct segvn_data, give page_t pointer " + "(if resident), or NULL.", + segvn_pages_walk_init, segvn_pages_walk_step, + segvn_pages_walk_fini, SEGVN_PAGES_ALL }, { "swapinfo", "walk swapinfo structures", swap_walk_init, swap_walk_step, NULL }, diff --git a/usr/src/cmd/mdb/common/modules/genunix/memory.c b/usr/src/cmd/mdb/common/modules/genunix/memory.c index e9ae1553ea..528040b9e5 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/memory.c +++ b/usr/src/cmd/mdb/common/modules/genunix/memory.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. */ #include <mdb/mdb_param.h> @@ -45,6 +44,7 @@ #endif #include "avl.h" +#include "memory.h" /* * Page walker. @@ -965,13 +965,11 @@ seg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) /*ARGSUSED*/ static int -pmap_walk_anon(uintptr_t addr, const struct anon *anon, int *nres) +pmap_walk_count_pages(uintptr_t addr, const void *data, void *out) { - uintptr_t pp = - mdb_page_lookup((uintptr_t)anon->an_vp, (u_offset_t)anon->an_off); + pgcnt_t *nres = out; - if (pp != NULL) - (*nres)++; + (*nres)++; return (WALK_NEXT); } @@ -982,35 +980,34 @@ pmap_walk_seg(uintptr_t addr, const struct seg *seg, uintptr_t segvn) mdb_printf("%0?p %0?p %7dk", addr, seg->s_base, seg->s_size / 1024); - if (segvn == (uintptr_t)seg->s_ops) { + if (segvn == (uintptr_t)seg->s_ops && seg->s_data != NULL) { struct segvn_data svn; - int nres = 0; + pgcnt_t nres = 0; + svn.vp = NULL; (void) mdb_vread(&svn, sizeof (svn), (uintptr_t)seg->s_data); - if (svn.amp == NULL) { - mdb_printf(" %8s", ""); - goto drive_on; - } - /* - * We've got an amp for this segment; walk through - * the amp, and determine mappings. + * Use the segvn_pages walker to find all of the in-core pages + * for this mapping. */ - if (mdb_pwalk("anon", (mdb_walk_cb_t)pmap_walk_anon, - &nres, (uintptr_t)svn.amp) == -1) - mdb_warn("failed to walk anon (amp=%p)", svn.amp); - - mdb_printf(" %7dk", (nres * PAGESIZE) / 1024); -drive_on: + if (mdb_pwalk("segvn_pages", pmap_walk_count_pages, &nres, + (uintptr_t)seg->s_data) == -1) { + mdb_warn("failed to walk segvn_pages (s_data=%p)", + seg->s_data); + } + mdb_printf(" %7ldk", (nres * PAGESIZE) / 1024); if (svn.vp != NULL) { char buf[29]; mdb_vnode2path((uintptr_t)svn.vp, buf, sizeof (buf)); mdb_printf(" %s", buf); - } else + } else { mdb_printf(" [ anon ]"); + } + } else { + mdb_printf(" %8s [ &%a ]", "?", seg->s_ops); } mdb_printf("\n"); @@ -1022,9 +1019,10 @@ pmap_walk_seg_quick(uintptr_t addr, const struct seg *seg, uintptr_t segvn) { mdb_printf("%0?p %0?p %7dk", addr, seg->s_base, seg->s_size / 1024); - if (segvn == (uintptr_t)seg->s_ops) { + if (segvn == (uintptr_t)seg->s_ops && seg->s_data != NULL) { struct segvn_data svn; + svn.vp = NULL; (void) mdb_vread(&svn, sizeof (svn), (uintptr_t)seg->s_data); if (svn.vp != NULL) { @@ -1032,6 +1030,8 @@ pmap_walk_seg_quick(uintptr_t addr, const struct seg *seg, uintptr_t segvn) } else { mdb_printf(" [ anon ]"); } + } else { + mdb_printf(" [ &%a ]", seg->s_ops); } mdb_printf("\n"); @@ -1086,15 +1086,19 @@ pmap(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) typedef struct anon_walk_data { uintptr_t *aw_levone; uintptr_t *aw_levtwo; - int aw_nlevone; - int aw_levone_ndx; - int aw_levtwo_ndx; + size_t aw_minslot; + size_t aw_maxslot; + pgcnt_t aw_nlevone; + pgcnt_t aw_levone_ndx; + size_t aw_levtwo_ndx; + struct anon_map *aw_ampp; struct anon_map aw_amp; - struct anon_hdr aw_ahp; + struct anon_hdr aw_ahp; + int aw_all; /* report all anon pointers, even NULLs */ } anon_walk_data_t; int -anon_walk_init(mdb_walk_state_t *wsp) +anon_walk_init_common(mdb_walk_state_t *wsp, ulong_t minslot, ulong_t maxslot) { anon_walk_data_t *aw; @@ -1104,6 +1108,7 @@ anon_walk_init(mdb_walk_state_t *wsp) } aw = mdb_alloc(sizeof (anon_walk_data_t), UM_SLEEP); + aw->aw_ampp = (struct anon_map *)wsp->walk_addr; if (mdb_vread(&aw->aw_amp, sizeof (aw->aw_amp), wsp->walk_addr) == -1) { mdb_warn("failed to read anon map at %p", wsp->walk_addr); @@ -1118,39 +1123,33 @@ anon_walk_init(mdb_walk_state_t *wsp) return (WALK_ERR); } + /* update min and maxslot with the given constraints */ + maxslot = MIN(maxslot, aw->aw_ahp.size); + minslot = MIN(minslot, maxslot); + if (aw->aw_ahp.size <= ANON_CHUNK_SIZE || (aw->aw_ahp.flags & ANON_ALLOC_FORCE)) { - aw->aw_nlevone = aw->aw_ahp.size; + aw->aw_nlevone = maxslot; + aw->aw_levone_ndx = minslot; aw->aw_levtwo = NULL; } else { aw->aw_nlevone = - (aw->aw_ahp.size + ANON_CHUNK_OFF) >> ANON_CHUNK_SHIFT; + (maxslot + ANON_CHUNK_OFF) >> ANON_CHUNK_SHIFT; + aw->aw_levone_ndx = 0; aw->aw_levtwo = mdb_zalloc(ANON_CHUNK_SIZE * sizeof (uintptr_t), UM_SLEEP); } aw->aw_levone = mdb_alloc(aw->aw_nlevone * sizeof (uintptr_t), UM_SLEEP); - - aw->aw_levone_ndx = 0; - aw->aw_levtwo_ndx = 0; + aw->aw_all = (wsp->walk_arg == ANON_WALK_ALL); mdb_vread(aw->aw_levone, aw->aw_nlevone * sizeof (uintptr_t), (uintptr_t)aw->aw_ahp.array_chunk); - if (aw->aw_levtwo != NULL) { - while (aw->aw_levone[aw->aw_levone_ndx] == NULL) { - aw->aw_levone_ndx++; - if (aw->aw_levone_ndx == aw->aw_nlevone) { - mdb_warn("corrupt anon; couldn't" - "find ptr to lev two map"); - goto out; - } - } - - mdb_vread(aw->aw_levtwo, ANON_CHUNK_SIZE * sizeof (uintptr_t), - aw->aw_levone[aw->aw_levone_ndx]); - } + aw->aw_levtwo_ndx = 0; + aw->aw_minslot = minslot; + aw->aw_maxslot = maxslot; out: wsp->walk_data = aw; @@ -1160,48 +1159,78 @@ out: int anon_walk_step(mdb_walk_state_t *wsp) { - int status; anon_walk_data_t *aw = (anon_walk_data_t *)wsp->walk_data; struct anon anon; uintptr_t anonptr; + ulong_t slot; -again: /* * Once we've walked through level one, we're done. */ - if (aw->aw_levone_ndx == aw->aw_nlevone) + if (aw->aw_levone_ndx >= aw->aw_nlevone) { return (WALK_DONE); + } if (aw->aw_levtwo == NULL) { anonptr = aw->aw_levone[aw->aw_levone_ndx]; aw->aw_levone_ndx++; } else { + if (aw->aw_levtwo_ndx == 0) { + uintptr_t levtwoptr; + + /* The first time through, skip to our first index. */ + if (aw->aw_levone_ndx == 0) { + aw->aw_levone_ndx = + aw->aw_minslot / ANON_CHUNK_SIZE; + aw->aw_levtwo_ndx = + aw->aw_minslot % ANON_CHUNK_SIZE; + } + + levtwoptr = (uintptr_t)aw->aw_levone[aw->aw_levone_ndx]; + + if (levtwoptr == NULL) { + if (!aw->aw_all) { + aw->aw_levtwo_ndx = 0; + aw->aw_levone_ndx++; + return (WALK_NEXT); + } + bzero(aw->aw_levtwo, + ANON_CHUNK_SIZE * sizeof (uintptr_t)); + + } else if (mdb_vread(aw->aw_levtwo, + ANON_CHUNK_SIZE * sizeof (uintptr_t), levtwoptr) == + -1) { + mdb_warn("unable to read anon_map %p's " + "second-level map %d at %p", + aw->aw_ampp, aw->aw_levone_ndx, + levtwoptr); + return (WALK_ERR); + } + } + slot = aw->aw_levone_ndx * ANON_CHUNK_SIZE + aw->aw_levtwo_ndx; anonptr = aw->aw_levtwo[aw->aw_levtwo_ndx]; - aw->aw_levtwo_ndx++; + /* update the indices for next time */ + aw->aw_levtwo_ndx++; if (aw->aw_levtwo_ndx == ANON_CHUNK_SIZE) { aw->aw_levtwo_ndx = 0; + aw->aw_levone_ndx++; + } - do { - aw->aw_levone_ndx++; - - if (aw->aw_levone_ndx == aw->aw_nlevone) - return (WALK_DONE); - } while (aw->aw_levone[aw->aw_levone_ndx] == NULL); - - mdb_vread(aw->aw_levtwo, ANON_CHUNK_SIZE * - sizeof (uintptr_t), - aw->aw_levone[aw->aw_levone_ndx]); + /* make sure the slot # is in the requested range */ + if (slot >= aw->aw_maxslot) { + return (WALK_DONE); } } if (anonptr != NULL) { mdb_vread(&anon, sizeof (anon), anonptr); - status = wsp->walk_callback(anonptr, &anon, wsp->walk_cbdata); - } else - goto again; - - return (status); + return (wsp->walk_callback(anonptr, &anon, wsp->walk_cbdata)); + } + if (aw->aw_all) { + return (wsp->walk_callback(NULL, NULL, wsp->walk_cbdata)); + } + return (WALK_NEXT); } void @@ -1216,6 +1245,319 @@ anon_walk_fini(mdb_walk_state_t *wsp) mdb_free(aw, sizeof (anon_walk_data_t)); } +int +anon_walk_init(mdb_walk_state_t *wsp) +{ + return (anon_walk_init_common(wsp, 0, ULONG_MAX)); +} + +int +segvn_anon_walk_init(mdb_walk_state_t *wsp) +{ + const uintptr_t svd_addr = wsp->walk_addr; + uintptr_t amp_addr; + uintptr_t seg_addr; + struct segvn_data svd; + struct anon_map amp; + struct seg seg; + + if (svd_addr == NULL) { + mdb_warn("segvn_anon walk doesn't support global walks\n"); + return (WALK_ERR); + } + if (mdb_vread(&svd, sizeof (svd), svd_addr) == -1) { + mdb_warn("segvn_anon walk: unable to read segvn_data at %p", + svd_addr); + return (WALK_ERR); + } + if (svd.amp == NULL) { + mdb_warn("segvn_anon walk: segvn_data at %p has no anon map\n", + svd_addr); + return (WALK_ERR); + } + amp_addr = (uintptr_t)svd.amp; + if (mdb_vread(&, sizeof (amp), amp_addr) == -1) { + mdb_warn("segvn_anon walk: unable to read amp %p for " + "segvn_data %p", amp_addr, svd_addr); + return (WALK_ERR); + } + seg_addr = (uintptr_t)svd.seg; + if (mdb_vread(&seg, sizeof (seg), seg_addr) == -1) { + mdb_warn("segvn_anon walk: unable to read seg %p for " + "segvn_data %p", seg_addr, svd_addr); + return (WALK_ERR); + } + if ((seg.s_size + (svd.anon_index << PAGESHIFT)) > amp.size) { + mdb_warn("anon map %p is too small for segment %p\n", + amp_addr, seg_addr); + return (WALK_ERR); + } + + wsp->walk_addr = amp_addr; + return (anon_walk_init_common(wsp, + svd.anon_index, svd.anon_index + (seg.s_size >> PAGESHIFT))); +} + + +typedef struct { + u_offset_t svs_offset; + uintptr_t svs_page; +} segvn_sparse_t; +#define SEGVN_MAX_SPARSE ((128 * 1024) / sizeof (segvn_sparse_t)) + +typedef struct { + uintptr_t svw_svdp; + struct segvn_data svw_svd; + struct seg svw_seg; + size_t svw_walkoff; + ulong_t svw_anonskip; + segvn_sparse_t *svw_sparse; + size_t svw_sparse_idx; + size_t svw_sparse_count; + size_t svw_sparse_size; + uint8_t svw_sparse_overflow; + uint8_t svw_all; +} segvn_walk_data_t; + +static int +segvn_sparse_fill(uintptr_t addr, const void *pp_arg, void *arg) +{ + segvn_walk_data_t *const svw = arg; + const page_t *const pp = pp_arg; + const u_offset_t offset = pp->p_offset; + segvn_sparse_t *const cur = + &svw->svw_sparse[svw->svw_sparse_count]; + + /* See if the page is of interest */ + if ((u_offset_t)(offset - svw->svw_svd.offset) >= svw->svw_seg.s_size) { + return (WALK_NEXT); + } + /* See if we have space for the new entry, then add it. */ + if (svw->svw_sparse_count >= svw->svw_sparse_size) { + svw->svw_sparse_overflow = 1; + return (WALK_DONE); + } + svw->svw_sparse_count++; + cur->svs_offset = offset; + cur->svs_page = addr; + return (WALK_NEXT); +} + +static int +segvn_sparse_cmp(const void *lp, const void *rp) +{ + const segvn_sparse_t *const l = lp; + const segvn_sparse_t *const r = rp; + + if (l->svs_offset < r->svs_offset) { + return (-1); + } + if (l->svs_offset > r->svs_offset) { + return (1); + } + return (0); +} + +/* + * Builds on the "anon_all" walker to walk all resident pages in a segvn_data + * structure. For segvn_datas without an anon structure, it just looks up + * pages in the vnode. For segvn_datas with an anon structure, NULL slots + * pass through to the vnode, and non-null slots are checked for residency. + */ +int +segvn_pages_walk_init(mdb_walk_state_t *wsp) +{ + segvn_walk_data_t *svw; + struct segvn_data *svd; + + if (wsp->walk_addr == NULL) { + mdb_warn("segvn walk doesn't support global walks\n"); + return (WALK_ERR); + } + + svw = mdb_zalloc(sizeof (*svw), UM_SLEEP); + svw->svw_svdp = wsp->walk_addr; + svw->svw_anonskip = 0; + svw->svw_sparse_idx = 0; + svw->svw_walkoff = 0; + svw->svw_all = (wsp->walk_arg == SEGVN_PAGES_ALL); + + if (mdb_vread(&svw->svw_svd, sizeof (svw->svw_svd), wsp->walk_addr) == + -1) { + mdb_warn("failed to read segvn_data at %p", wsp->walk_addr); + mdb_free(svw, sizeof (*svw)); + return (WALK_ERR); + } + + svd = &svw->svw_svd; + if (mdb_vread(&svw->svw_seg, sizeof (svw->svw_seg), + (uintptr_t)svd->seg) == -1) { + mdb_warn("failed to read seg at %p (from %p)", + svd->seg, &((struct segvn_data *)(wsp->walk_addr))->seg); + mdb_free(svw, sizeof (*svw)); + return (WALK_ERR); + } + + if (svd->amp == NULL && svd->vp == NULL) { + /* make the walk terminate immediately; no pages */ + svw->svw_walkoff = svw->svw_seg.s_size; + + } else if (svd->amp == NULL && + (svw->svw_seg.s_size >> PAGESHIFT) >= SEGVN_MAX_SPARSE) { + /* + * If we don't have an anon pointer, and the segment is large, + * we try to load the in-memory pages into a fixed-size array, + * which is then sorted and reported directly. This is much + * faster than doing a mdb_page_lookup() for each possible + * offset. + * + * If the allocation fails, or there are too many pages + * in-core, we fall back to looking up the pages individually. + */ + svw->svw_sparse = mdb_alloc( + SEGVN_MAX_SPARSE * sizeof (*svw->svw_sparse), UM_NOSLEEP); + if (svw->svw_sparse != NULL) { + svw->svw_sparse_size = SEGVN_MAX_SPARSE; + + if (mdb_pwalk("page", segvn_sparse_fill, svw, + (uintptr_t)svd->vp) == -1 || + svw->svw_sparse_overflow) { + mdb_free(svw->svw_sparse, SEGVN_MAX_SPARSE * + sizeof (*svw->svw_sparse)); + svw->svw_sparse = NULL; + } else { + qsort(svw->svw_sparse, svw->svw_sparse_count, + sizeof (*svw->svw_sparse), + segvn_sparse_cmp); + } + } + + } else if (svd->amp != NULL) { + const char *const layer = (!svw->svw_all && svd->vp == NULL) ? + "segvn_anon" : "segvn_anon_all"; + /* + * If we're not printing all offsets, and the segvn_data has + * no backing VP, we can use the "segvn_anon" walker, which + * efficiently skips NULL slots. + * + * Otherwise, we layer over the "segvn_anon_all" walker + * (which reports all anon slots, even NULL ones), so that + * segvn_pages_walk_step() knows the precise offset for each + * element. It uses that offset information to look up the + * backing pages for NULL anon slots. + */ + if (mdb_layered_walk(layer, wsp) == -1) { + mdb_warn("segvn_pages: failed to layer \"%s\" " + "for segvn_data %p", layer, svw->svw_svdp); + mdb_free(svw, sizeof (*svw)); + return (WALK_ERR); + } + } + + wsp->walk_data = svw; + return (WALK_NEXT); +} + +int +segvn_pages_walk_step(mdb_walk_state_t *wsp) +{ + segvn_walk_data_t *const svw = wsp->walk_data; + struct seg *const seg = &svw->svw_seg; + struct segvn_data *const svd = &svw->svw_svd; + uintptr_t pp; + page_t page; + + /* If we've walked off the end of the segment, we're done. */ + if (svw->svw_walkoff >= seg->s_size) { + return (WALK_DONE); + } + + /* + * If we've got a sparse page array, just send it directly. + */ + if (svw->svw_sparse != NULL) { + u_offset_t off; + + if (svw->svw_sparse_idx >= svw->svw_sparse_count) { + pp = NULL; + if (!svw->svw_all) { + return (WALK_DONE); + } + } else { + segvn_sparse_t *const svs = + &svw->svw_sparse[svw->svw_sparse_idx]; + off = svs->svs_offset - svd->offset; + if (svw->svw_all && svw->svw_walkoff != off) { + pp = NULL; + } else { + pp = svs->svs_page; + svw->svw_sparse_idx++; + } + } + + } else if (svd->amp == NULL || wsp->walk_addr == NULL) { + /* + * If there's no anon, or the anon slot is NULL, look up + * <vp, offset>. + */ + if (svd->vp != NULL) { + pp = mdb_page_lookup((uintptr_t)svd->vp, + svd->offset + svw->svw_walkoff); + } else { + pp = NULL; + } + + } else { + const struct anon *const anon = wsp->walk_layer; + + /* + * We have a "struct anon"; if it's not swapped out, + * look up the page. + */ + if (anon->an_vp != NULL || anon->an_off != 0) { + pp = mdb_page_lookup((uintptr_t)anon->an_vp, + anon->an_off); + if (pp == 0 && mdb_get_state() != MDB_STATE_RUNNING) { + mdb_warn("walk segvn_pages: segvn_data %p " + "offset %ld, anon page <%p, %llx> not " + "found.\n", svw->svw_svdp, svw->svw_walkoff, + anon->an_vp, anon->an_off); + } + } else { + if (anon->an_pvp == NULL) { + mdb_warn("walk segvn_pages: useless struct " + "anon at %p\n", wsp->walk_addr); + } + pp = NULL; /* nothing at this offset */ + } + } + + svw->svw_walkoff += PAGESIZE; /* Update for the next call */ + if (pp != NULL) { + if (mdb_vread(&page, sizeof (page_t), pp) == -1) { + mdb_warn("unable to read page_t at %#lx", pp); + return (WALK_ERR); + } + return (wsp->walk_callback(pp, &page, wsp->walk_cbdata)); + } + if (svw->svw_all) { + return (wsp->walk_callback(NULL, NULL, wsp->walk_cbdata)); + } + return (WALK_NEXT); +} + +void +segvn_pages_walk_fini(mdb_walk_state_t *wsp) +{ + segvn_walk_data_t *const svw = wsp->walk_data; + + if (svw->svw_sparse != NULL) { + mdb_free(svw->svw_sparse, SEGVN_MAX_SPARSE * + sizeof (*svw->svw_sparse)); + } + mdb_free(svw, sizeof (*svw)); +} + /* * Grumble, grumble. */ diff --git a/usr/src/cmd/mdb/common/modules/genunix/memory.h b/usr/src/cmd/mdb/common/modules/genunix/memory.h index b24024b3a4..a6f6130ed7 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/memory.h +++ b/usr/src/cmd/mdb/common/modules/genunix/memory.h @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _MEMORY_H @@ -46,10 +45,19 @@ int page_num2pp(uintptr_t, uint_t, int, const mdb_arg_t *); int seg_walk_init(mdb_walk_state_t *); int seg(uintptr_t, uint_t, int, const mdb_arg_t *); +#define SEGVN_PAGES_RESIDENT (void *)(uintptr_t)0 +#define SEGVN_PAGES_ALL (void *)(uintptr_t)1 +int segvn_pages_walk_init(mdb_walk_state_t *); +int segvn_pages_walk_step(mdb_walk_state_t *); +void segvn_pages_walk_fini(mdb_walk_state_t *); + int vnode2smap(uintptr_t, uint_t, int, const mdb_arg_t *); int addr2smap(uintptr_t, uint_t, int, const mdb_arg_t *); +#define ANON_WALK_ALLOC (void *)(uintptr_t)0 +#define ANON_WALK_ALL (void *)(uintptr_t)1 int anon_walk_init(mdb_walk_state_t *); +int segvn_anon_walk_init(mdb_walk_state_t *); int anon_walk_step(mdb_walk_state_t *); void anon_walk_fini(mdb_walk_state_t *); int pmap(uintptr_t, uint_t, int, const mdb_arg_t *); diff --git a/usr/src/cmd/mdb/common/modules/genunix/vfs.c b/usr/src/cmd/mdb/common/modules/genunix/vfs.c index 8001c41b3c..3ea333ddf8 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/vfs.c +++ b/usr/src/cmd/mdb/common/modules/genunix/vfs.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include <mdb/mdb_modapi.h> @@ -723,7 +722,7 @@ sctp_getpeeraddr(sctp_t *sctp, struct sockaddr *addr) mdb_warn("failed to read sctp primary faddr"); return (-1); } - faddr = sctp_primary.faddr; + faddr = sctp_primary.sf_faddr; switch (connp->conn_family) { case AF_INET: diff --git a/usr/src/cmd/mdb/common/modules/genunix/zone.c b/usr/src/cmd/mdb/common/modules/genunix/zone.c index 28912479b3..96f6b598ec 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/zone.c +++ b/usr/src/cmd/mdb/common/modules/genunix/zone.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include <mdb/mdb_param.h> @@ -39,6 +38,22 @@ #define ZONE_PATHLEN 40 #endif +/* + * Names corresponding to zone_status_t values in sys/zone.h + */ +char *zone_status_names[] = { + "uninitialized", /* ZONE_IS_UNINITIALIZED */ + "initialized", /* ZONE_IS_INITIALIZED */ + "ready", /* ZONE_IS_READY */ + "booting", /* ZONE_IS_BOOTING */ + "running", /* ZONE_IS_RUNNING */ + "shutting_down", /* ZONE_IS_SHUTTING_DOWN */ + "empty", /* ZONE_IS_EMPTY */ + "down", /* ZONE_IS_DOWN */ + "dying", /* ZONE_IS_DYING */ + "dead" /* ZONE_IS_DEAD */ +}; + int zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { @@ -46,8 +61,10 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) char name[ZONE_NAMELEN]; char path[ZONE_PATHLEN]; int len; + uint_t vopt_given; + uint_t ropt_given; - if (argc != 0) + if (argc > 2) return (DCMD_USAGE); if (!(flags & DCMD_ADDRSPEC)) { @@ -57,10 +74,38 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) } return (DCMD_OK); } + + /* + * Get the optional -r (reference counts) and -v (verbose output) + * arguments. + */ + vopt_given = FALSE; + ropt_given = FALSE; + if (argc > 0 && mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, + &vopt_given, 'r', MDB_OPT_SETBITS, TRUE, &ropt_given, NULL) != argc) + return (DCMD_USAGE); + + /* + * -v can only be specified with -r. + */ + if (vopt_given == TRUE && ropt_given == FALSE) + return (DCMD_USAGE); + + /* + * Print a table header, if necessary. + */ if (DCMD_HDRSPEC(flags)) { - mdb_printf("%<u>%?s %6s %-20s %-s%</u>\n", - "ADDR", "ID", "NAME", "PATH"); + if (ropt_given == FALSE) + mdb_printf("%<u>%?s %6s %-13s %-20s %-s%</u>\n", + "ADDR", "ID", "STATUS", "NAME", "PATH"); + else + mdb_printf("%<u>%?s %6s %10s %10s %-20s%</u>\n", + "ADDR", "ID", "REFS", "CREFS", "NAME"); } + + /* + * Read the zone_t structure at the given address and read its name. + */ if (mdb_vread(&zn, sizeof (zone_t), addr) == -1) { mdb_warn("can't read zone_t structure at %p", addr); return (DCMD_ERR); @@ -72,14 +117,110 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) } else { (void) strcpy(name, "??"); } - len = mdb_readstr(path, ZONE_PATHLEN, (uintptr_t)zn.zone_rootpath); - if (len > 0) { - if (len == ZONE_PATHLEN) - (void) strcpy(&path[len - 4], "..."); + + if (ropt_given == FALSE) { + char *statusp; + + /* + * Default display + * Fetch the zone's path and print the results. + */ + len = mdb_readstr(path, ZONE_PATHLEN, + (uintptr_t)zn.zone_rootpath); + if (len > 0) { + if (len == ZONE_PATHLEN) + (void) strcpy(&path[len - 4], "..."); + } else { + (void) strcpy(path, "??"); + } + if (zn.zone_status >= ZONE_IS_UNINITIALIZED && zn.zone_status <= + ZONE_IS_DEAD) + statusp = zone_status_names[zn.zone_status]; + else + statusp = "???"; + mdb_printf("%0?p %6d %-13s %-20s %s\n", addr, zn.zone_id, + statusp, name, path); } else { - (void) strcpy(path, "??"); + /* + * Display the zone's reference counts. + * Display the zone's subsystem-specific reference counts if + * the user specified the '-v' option. + */ + mdb_printf("%0?p %6d %10u %10u %-20s\n", addr, zn.zone_id, + zn.zone_ref, zn.zone_cred_ref, name); + if (vopt_given == TRUE) { + GElf_Sym subsys_names_sym; + uintptr_t **zone_ref_subsys_names; + uint_t num_subsys; + uint_t n; + + /* + * Read zone_ref_subsys_names from the kernel image. + */ + if (mdb_lookup_by_name("zone_ref_subsys_names", + &subsys_names_sym) != 0) { + mdb_warn("can't find zone_ref_subsys_names"); + return (DCMD_ERR); + } + if (subsys_names_sym.st_size != ZONE_REF_NUM_SUBSYS * + sizeof (char *)) { + mdb_warn("number of subsystems in target " + "differs from what mdb expects (mismatched" + " kernel versions?)"); + if (subsys_names_sym.st_size < + ZONE_REF_NUM_SUBSYS * sizeof (char *)) + num_subsys = subsys_names_sym.st_size / + sizeof (char *); + else + num_subsys = ZONE_REF_NUM_SUBSYS; + } else { + num_subsys = ZONE_REF_NUM_SUBSYS; + } + if ((zone_ref_subsys_names = mdb_alloc( + subsys_names_sym.st_size, UM_GC)) == NULL) { + mdb_warn("out of memory"); + return (DCMD_ERR); + } + if (mdb_readvar(zone_ref_subsys_names, + "zone_ref_subsys_names") == -1) { + mdb_warn("can't find zone_ref_subsys_names"); + return (DCMD_ERR); + } + + /* + * Display each subsystem's reference count if it's + * nonzero. + */ + mdb_inc_indent(7); + for (n = 0; n < num_subsys; ++n) { + char subsys_name[16]; + + /* + * Skip subsystems lacking outstanding + * references. + */ + if (zn.zone_subsys_ref[n] == 0) + continue; + + /* + * Each subsystem's name must be read from + * the target's image. + */ + if (mdb_readstr(subsys_name, + sizeof (subsys_name), + (uintptr_t)zone_ref_subsys_names[n]) == + -1) { + mdb_warn("unable to read subsystem name" + " from zone_ref_subsys_names[%u]", + n); + return (DCMD_ERR); + } + mdb_printf("%15s: %10u\n", subsys_name, + zn.zone_subsys_ref[n]); + } + mdb_dec_indent(7); + } } - mdb_printf("%0?p %6d %-20s %s\n", addr, zn.zone_id, name, path); return (DCMD_OK); } diff --git a/usr/src/cmd/mdb/common/modules/libc/findstack_subr.c b/usr/src/cmd/mdb/common/modules/libc/findstack_subr.c new file mode 100644 index 0000000000..01e4729475 --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/libc/findstack_subr.c @@ -0,0 +1,382 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include <mdb/mdb_modapi.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/avl.h> +#include <sys/lwp.h> +#include <thr_uberdata.h> +#include <stddef.h> +#include "findstack.h" + +#if defined(__i386) || defined(__amd64) +struct rwindow { + uintptr_t rw_fp; + uintptr_t rw_rtn; +}; +#endif + +#ifndef STACK_BIAS +#define STACK_BIAS 0 +#endif + +#ifdef __amd64 +#define STACKS_REGS_FP "rbp" +#define STACKS_REGS_RC "rip" +#else +#ifdef __i386 +#define STACKS_REGS_FP "ebp" +#define STACKS_REGS_RC "eip" +#else +#define STACKS_REGS_FP "fp" +#define STACKS_REGS_RC "pc" +#endif +#endif + +#define STACKS_SOBJ_MX (uintptr_t)"MX" +#define STACKS_SOBJ_CV (uintptr_t)"CV" + +int +thread_text_to_state(const char *state, uint_t *out) +{ + if (strcmp(state, "PARKED") == 0) { + *out = B_TRUE; + } else if (strcmp(state, "UNPARKED") == 0) { + *out = B_FALSE; + } else if (strcmp(state, "FREE") == 0) { + /* + * When run with "-i", ::stacks filters out "FREE" threads. + * We therefore need to recognize "FREE", and set it to a + * value that will never match fsi_tstate. + */ + *out = UINT_MAX; + } else { + return (-1); + } + + return (0); +} + +void +thread_state_to_text(uint_t state, char *out, size_t out_sz) +{ + (void) snprintf(out, out_sz, state ? "PARKED" : "UNPARKED"); +} + +int +sobj_text_to_ops(const char *name, uintptr_t *sobj_ops_out) +{ + if (strcmp(name, "MX") == 0) { + *sobj_ops_out = STACKS_SOBJ_MX; + } else if (strcmp(name, "CV") == 0) { + *sobj_ops_out = STACKS_SOBJ_CV; + } else { + mdb_warn("sobj \"%s\" not recognized\n", name); + return (-1); + } + + return (0); +} + +void +sobj_ops_to_text(uintptr_t addr, char *out, size_t sz) +{ + (void) snprintf(out, sz, "%s", addr == NULL ? "<none>" : (char *)addr); +} + +static int +stacks_module_callback(mdb_object_t *obj, void *arg) +{ + stacks_module_t *smp = arg; + boolean_t match = (strcmp(obj->obj_name, smp->sm_name) == 0); + char *suffix = ".so"; + const char *s, *next; + size_t len; + + if (smp->sm_size != 0) + return (0); + + /* + * It doesn't match the name, but -- for convenience -- we want to + * allow matches before ".so.[suffix]". An aside: why doesn't + * strrstr() exist? (Don't google that. I'm serious, don't do it. + * If you do, and you read the thread of "why doesn't strrstr() exist?" + * circa 2005 you will see things that you will NEVER be able to unsee!) + */ + if (!match && (s = strstr(obj->obj_name, suffix)) != NULL) { + while ((next = strstr(s + 1, suffix)) != NULL) { + s = next; + continue; + } + + len = s - obj->obj_name; + + match = (strncmp(smp->sm_name, obj->obj_name, len) == 0 && + smp->sm_name[len] == '\0'); + } + + /* + * If we have a library that has the libc directory in the path, we + * want to match against anything that would match libc.so.1. (This + * is necessary to be able to easily deal with libc implementations + * that have alternate hardware capabilities.) + */ + if (!match && strstr(obj->obj_fullname, "/libc/") != NULL) { + mdb_object_t libc = *obj; + + libc.obj_name = "libc.so.1"; + libc.obj_fullname = ""; + + return (stacks_module_callback(&libc, arg)); + } + + if (match) { + smp->sm_text = obj->obj_base; + smp->sm_size = obj->obj_size; + } + + return (0); +} + +int +stacks_module(stacks_module_t *smp) +{ + if (mdb_object_iter(stacks_module_callback, smp) != 0) + return (-1); + + return (0); +} + +typedef struct stacks_ulwp { + avl_node_t sulwp_node; + lwpid_t sulwp_id; + uintptr_t sulwp_addr; +} stacks_ulwp_t; + +boolean_t stacks_ulwp_initialized; +avl_tree_t stacks_ulwp_byid; + +/*ARGSUSED*/ +int +stacks_ulwp_walk(uintptr_t addr, ulwp_t *ulwp, void *ignored) +{ + stacks_ulwp_t *sulwp = mdb_alloc(sizeof (stacks_ulwp_t), UM_SLEEP); + + sulwp->sulwp_id = ulwp->ul_lwpid; + sulwp->sulwp_addr = addr; + + if (avl_find(&stacks_ulwp_byid, sulwp, NULL) != NULL) { + mdb_warn("found multiple LWPs with ID %d!", ulwp->ul_lwpid); + return (WALK_ERR); + } + + avl_add(&stacks_ulwp_byid, sulwp); + + return (WALK_NEXT); +} + +static int +stacks_ulwp_compare(const void *l, const void *r) +{ + const stacks_ulwp_t *lhs = l; + const stacks_ulwp_t *rhs = r; + + if (lhs->sulwp_id > rhs->sulwp_id) + return (1); + + if (lhs->sulwp_id < rhs->sulwp_id) + return (-1); + + return (0); +} + +/*ARGSUSED*/ +int +stacks_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings) +{ + mdb_reg_t reg; + uintptr_t fp; + struct rwindow frame; + avl_tree_t *tree = &stacks_ulwp_byid; + stacks_ulwp_t *sulwp, cmp; + ulwp_t ulwp; + + fsip->fsi_failed = 0; + fsip->fsi_pc = 0; + fsip->fsi_sp = 0; + fsip->fsi_depth = 0; + fsip->fsi_overflow = 0; + + if (!stacks_ulwp_initialized) { + avl_create(tree, stacks_ulwp_compare, sizeof (stacks_ulwp_t), + offsetof(stacks_ulwp_t, sulwp_node)); + + if (mdb_walk("ulwp", + (mdb_walk_cb_t)stacks_ulwp_walk, NULL) != 0) { + mdb_warn("couldn't walk 'ulwp'"); + return (-1); + } + + stacks_ulwp_initialized = B_TRUE; + } + + bzero(&cmp, sizeof (cmp)); + cmp.sulwp_id = (lwpid_t)addr; + + if ((sulwp = avl_find(tree, &cmp, NULL)) == NULL) { + mdb_warn("couldn't find ulwp_t for tid %d\n", cmp.sulwp_id); + return (-1); + } + + if (mdb_vread(&ulwp, sizeof (ulwp), sulwp->sulwp_addr) == -1) { + mdb_warn("couldn't read ulwp_t for tid %d at %p", + cmp.sulwp_id, sulwp->sulwp_addr); + return (-1); + } + + fsip->fsi_tstate = ulwp.ul_sleepq != NULL; + fsip->fsi_sobj_ops = (uintptr_t)(ulwp.ul_sleepq == NULL ? NULL : + (ulwp.ul_qtype == MX ? STACKS_SOBJ_MX : STACKS_SOBJ_CV)); + + if (mdb_getareg(addr, STACKS_REGS_FP, ®) != 0) { + mdb_warn("couldn't read frame pointer for thread 0x%p", addr); + return (-1); + } + + fsip->fsi_sp = fp = (uintptr_t)reg; + +#if !defined(__i386) + if (mdb_getareg(addr, STACKS_REGS_RC, ®) != 0) { + mdb_warn("couldn't read program counter for thread 0x%p", addr); + return (-1); + } + + fsip->fsi_pc = (uintptr_t)reg; +#endif + + while (fp != NULL) { + if (mdb_vread(&frame, sizeof (frame), fp) == -1) { + mdb_warn("couldn't read frame for thread 0x%p at %p", + addr, fp); + return (-1); + } + + if (frame.rw_rtn == NULL) + break; + + if (fsip->fsi_depth < fsip->fsi_max_depth) { + fsip->fsi_stack[fsip->fsi_depth++] = frame.rw_rtn; + } else { + fsip->fsi_overflow = 1; + break; + } + + fp = frame.rw_fp + STACK_BIAS; + } + + return (0); +} + +void +stacks_findstack_cleanup() +{ + avl_tree_t *tree = &stacks_ulwp_byid; + void *cookie = NULL; + stacks_ulwp_t *sulwp; + + if (!stacks_ulwp_initialized) + return; + + while ((sulwp = avl_destroy_nodes(tree, &cookie)) != NULL) + mdb_free(sulwp, sizeof (stacks_ulwp_t)); + + bzero(tree, sizeof (*tree)); + stacks_ulwp_initialized = B_FALSE; +} + +void +stacks_help(void) +{ + mdb_printf( +"::stacks processes all of the thread stacks in the process, grouping\n" +"together threads which have the same:\n" +"\n" +" * Thread state,\n" +" * Sync object type, and\n" +" * PCs in their stack trace.\n" +"\n" +"The default output (no address or options) is just a dump of the thread\n" +"groups in the process. For a view of active threads, use \"::stacks -i\",\n" +"which filters out threads sleeping on a CV. More general filtering options\n" +"are described below, in the \"FILTERS\" section.\n" +"\n" +"::stacks can be used in a pipeline. The input to ::stacks is one or more\n" +"thread IDs. When output into a pipe, ::stacks prints all of the threads \n" +"input, filtered by the given filtering options. This means that multiple\n" +"::stacks invocations can be piped together to achieve more complicated\n" +"filters. For example, to get threads which have both '__door_return' and\n" +"'mutex_lock' in their stack trace, you could do:\n" +"\n" +" ::stacks -c __door_return | ::stacks -c mutex_lock\n" +"\n" +"To get the full list of threads in each group, use the '-a' flag:\n" +"\n" +" ::stacks -a\n" +"\n"); + mdb_dec_indent(2); + mdb_printf("%<b>OPTIONS%</b>\n"); + mdb_inc_indent(2); + mdb_printf("%s", +" -a Print all of the grouped threads, instead of just a count.\n" +" -f Force a re-run of the thread stack gathering.\n" +" -v Be verbose about thread stack gathering.\n" +"\n"); + mdb_dec_indent(2); + mdb_printf("%<b>FILTERS%</b>\n"); + mdb_inc_indent(2); + mdb_printf("%s", +" -i Show active threads; equivalent to '-S CV'.\n" +" -c func[+offset]\n" +" Only print threads whose stacks contain func/func+offset.\n" +" -C func[+offset]\n" +" Only print threads whose stacks do not contain func/func+offset.\n" +" -m module\n" +" Only print threads whose stacks contain functions from module.\n" +" -M module\n" +" Only print threads whose stacks do not contain functions from\n" +" module.\n" +" -s {type | ALL}\n" +" Only print threads which are on a 'type' synchronization object\n" +" (SOBJ).\n" +" -S {type | ALL}\n" +" Only print threads which are not on a 'type' SOBJ.\n" +" -t tstate\n" +" Only print threads which are in thread state 'tstate'.\n" +" -T tstate\n" +" Only print threads which are not in thread state 'tstate'.\n" +"\n"); +} diff --git a/usr/src/cmd/mdb/common/modules/libc/libc.c b/usr/src/cmd/mdb/common/modules/libc/libc.c index 5d0debcedf..da8bbc6828 100644 --- a/usr/src/cmd/mdb/common/modules/libc/libc.c +++ b/usr/src/cmd/mdb/common/modules/libc/libc.c @@ -32,6 +32,7 @@ #include <setjmp.h> #include <string.h> #include <thr_uberdata.h> +#include "findstack.h" static const char * stack_flags(const stack_t *sp) @@ -844,11 +845,12 @@ d_uberdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) prt_addr(uberdata.ulwp_replace_last, 1), prt_addr(uberdata.atforklist, 0)); - HD("robustlocks robustlist"); - mdb_printf(OFFSTR "%s %s\n", + HD("robustlocks robustlist progname"); + mdb_printf(OFFSTR "%s %s %s\n", OFFSET(robustlocks), prt_addr(uberdata.robustlocks, 1), - prt_addr(uberdata.robustlist, 0)); + prt_addr(uberdata.robustlist, 1), + prt_addr(uberdata.progname, 0)); HD("tdb_bootstrap tdb_sync_addr_hash tdb_'count tdb_'fail"); mdb_printf(OFFSTR "%s %s %-10d %d\n", @@ -981,10 +983,76 @@ whatis_run_ulwps(mdb_whatis_t *w, void *arg) * ==================== threads ========================== */ +int +stacks_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + int rval = stacks(addr, flags, argc, argv); + + /* + * For the user-level variant of ::stacks, we don't bother caching + * state, as even a very large program is unlikely to compare to the + * kernel in terms of number of threads. (And if you find yourself + * here in anger, frustrated about how long ::stacks is running on + * your galactically complicated zillion-thread program, hopefully + * you will find some solace in the irony. Okay, probably not...) + */ + stacks_cleanup(B_TRUE); + return (rval); +} + +typedef struct tid2ulwp_walk { + lwpid_t t2u_tid; + uintptr_t t2u_lwp; + boolean_t t2u_found; +} tid2ulwp_walk_t; + +/*ARGSUSED*/ +static int +tid2ulwp_walk(uintptr_t addr, ulwp_t *ulwp, tid2ulwp_walk_t *t2u) +{ + if (ulwp->ul_lwpid == t2u->t2u_tid) { + t2u->t2u_lwp = addr; + t2u->t2u_found = B_TRUE; + return (WALK_DONE); + } + + return (WALK_NEXT); +} + +/*ARGSUSED*/ +static int +tid2ulwp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + tid2ulwp_walk_t t2u; + + if (argc != 0) + return (DCMD_USAGE); + + bzero(&t2u, sizeof (t2u)); + t2u.t2u_tid = (lwpid_t)addr; + + if (mdb_walk("ulwp", (mdb_walk_cb_t)tid2ulwp_walk, &t2u) != 0) { + mdb_warn("can't walk 'ulwp'"); + return (DCMD_ERR); + } + + if (!t2u.t2u_found) { + mdb_warn("thread ID %d not found", t2u.t2u_tid); + return (DCMD_ERR); + } + + mdb_printf("%p\n", t2u.t2u_lwp); + + return (DCMD_OK); +} + static const mdb_dcmd_t dcmds[] = { { "jmp_buf", ":", "print jmp_buf contents", d_jmp_buf, NULL }, { "sigjmp_buf", ":", "print sigjmp_buf contents", d_sigjmp_buf, NULL }, { "siginfo", ":", "print siginfo_t structure", d_siginfo, NULL }, + { "stacks", "?[-afiv] [-c func] [-C func] [-m module] [-M module] ", + "print unique thread stacks", stacks_dcmd, stacks_help }, + { "tid2ulwp", "?", "convert TID to ulwp_t address", tid2ulwp }, { "ucontext", ":", "print ucontext_t structure", d_ucontext, NULL }, { "ulwp", ":", "print ulwp_t structure", d_ulwp, NULL }, { "uberdata", ":", "print uberdata_t structure", d_uberdata, NULL }, @@ -998,6 +1066,8 @@ static const mdb_walker_t walkers[] = { oldc_walk_init, oldc_walk_step, oldc_walk_fini, NULL }, { "ulwps", "walk list of ulwp_t pointers", ulwp_walk_init, ulwp_walk_step, NULL, NULL }, + { "ulwp", "walk list of ulwp_t pointers", + ulwp_walk_init, ulwp_walk_step, NULL, NULL }, { NULL } }; diff --git a/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c b/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c index ea9e747ba7..74e0396226 100644 --- a/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c +++ b/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c @@ -31,7 +31,6 @@ * to zero. */ - #include <mdb/mdb_target.h> #include <mdb/mdb_param.h> #include <mdb/mdb_modapi.h> @@ -88,6 +87,8 @@ uintptr_t _mdb_ks_argsbase; unsigned long _mdb_ks_msg_bsize; unsigned long _mdb_ks_defaultstksz; int _mdb_ks_ncpu; +int _mdb_ks_ncpu_log2; +int _mdb_ks_ncpu_p2; /* * In-core copy of DNLC information: @@ -97,10 +98,19 @@ int _mdb_ks_ncpu; #define MDB_DNLC_NCACHE_SZ(ncp) (sizeof (ncache_t) + (ncp)->namlen) #define MDB_DNLC_MAX_RETRY 4 - static ncache_t **dnlc_hash; /* mdbs hash array of dnlc entries */ /* + * copy of page_hash-related data + */ +static int page_hash_loaded; +static long mdb_page_hashsz; +static uint_t mdb_page_hashsz_shift; /* Needed for PAGE_HASH_FUNC */ +static uintptr_t mdb_page_hash; /* base address of page hash */ +#define page_hashsz mdb_page_hashsz +#define page_hashsz_shift mdb_page_hashsz_shift + +/* * This will be the location of the vnodeops pointer for "autofs_vnodeops" * The pointer still needs to be read with mdb_vread() to get the location * of the vnodeops structure for autofs. @@ -624,32 +634,78 @@ out: return (cpu); } +static int +page_hash_load(void) +{ + if (page_hash_loaded) { + return (1); + } + + if (mdb_readvar(&mdb_page_hashsz, "page_hashsz") == -1) { + mdb_warn("unable to read page_hashsz"); + return (0); + } + if (mdb_readvar(&mdb_page_hashsz_shift, "page_hashsz_shift") == -1) { + mdb_warn("unable to read page_hashsz_shift"); + return (0); + } + if (mdb_readvar(&mdb_page_hash, "page_hash") == -1) { + mdb_warn("unable to read page_hash"); + return (0); + } + + page_hash_loaded = 1; /* zeroed on state change */ + return (1); +} + uintptr_t mdb_page_lookup(uintptr_t vp, u_offset_t offset) { - long page_hashsz, ndx; - int page_hashsz_shift; /* Needed for PAGE_HASH_FUNC */ - uintptr_t page_hash, pp; + size_t ndx; + uintptr_t page_hash_entry, pp; - if (mdb_readvar(&page_hashsz, "page_hashsz") == -1 || - mdb_readvar(&page_hashsz_shift, "page_hashsz_shift") == -1 || - mdb_readvar(&page_hash, "page_hash") == -1) + if (!page_hash_loaded && !page_hash_load()) { return (NULL); + } ndx = PAGE_HASH_FUNC(vp, offset); - page_hash += ndx * sizeof (uintptr_t); + page_hash_entry = mdb_page_hash + ndx * sizeof (uintptr_t); - mdb_vread(&pp, sizeof (pp), page_hash); + if (mdb_vread(&pp, sizeof (pp), page_hash_entry) < 0) { + mdb_warn("unable to read page_hash[%ld] (%p)", ndx, + page_hash_entry); + return (NULL); + } while (pp != NULL) { page_t page; + long nndx; - mdb_vread(&page, sizeof (page), pp); + if (mdb_vread(&page, sizeof (page), pp) < 0) { + mdb_warn("unable to read page_t at %p", pp); + return (NULL); + } if ((uintptr_t)page.p_vnode == vp && (uint64_t)page.p_offset == offset) return (pp); + /* + * Double check that the pages actually hash to the + * bucket we're searching. If not, our version of + * PAGE_HASH_FUNC() doesn't match the kernel's, and we're + * not going to be able to find the page. The most + * likely reason for this that mdb_ks doesn't match the + * kernel we're running against. + */ + nndx = PAGE_HASH_FUNC(page.p_vnode, page.p_offset); + if (page.p_vnode != NULL && nndx != ndx) { + mdb_warn("mdb_page_lookup: mdb_ks PAGE_HASH_FUNC() " + "mismatch: in bucket %ld, but page %p hashes to " + "bucket %ld\n", ndx, pp, nndx); + return (NULL); + } + pp = (uintptr_t)page.p_hash; } @@ -1164,6 +1220,10 @@ update_vars(void *arg) (void) mdb_readvar(&_mdb_ks_msg_bsize, "_msg_bsize"); (void) mdb_readvar(&_mdb_ks_defaultstksz, "_defaultstksz"); (void) mdb_readvar(&_mdb_ks_ncpu, "_ncpu"); + (void) mdb_readvar(&_mdb_ks_ncpu_log2, "_ncpu_log2"); + (void) mdb_readvar(&_mdb_ks_ncpu_p2, "_ncpu_p2"); + + page_hash_loaded = 0; /* invalidate cached page_hash state */ } const mdb_modinfo_t * diff --git a/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c b/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c index 24785a7445..30ec30cbb6 100644 --- a/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c +++ b/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c @@ -1533,9 +1533,6 @@ outbound_iomb_opcode(uint32_t opcode) case PMCOUT_DEVICE_HANDLE_ARRIVED: return ("DEVICE_HANDLE_ARRIVED"); break; - case PMCOUT_SMP_REQUEST_RECEIVED: - return ("SMP_REQUEST_RECEIVED"); - break; case PMCOUT_SSP_REQUEST_RECEIVED: return ("SSP_REQUEST_RECEIVED"); break; @@ -1611,13 +1608,196 @@ outbound_iomb_opcode(uint32_t opcode) } } +static uint32_t +get_devid_from_ob_iomb(struct pmcs_hw ss, uint32_t *qentryp, uint16_t opcode) +{ + uint32_t devid = PMCS_INVALID_DEVICE_ID; + + switch (opcode) { + /* + * These are obtained via the HTAG which is in word 1 + */ + case PMCOUT_SSP_COMPLETION: + case PMCOUT_SMP_COMPLETION: + case PMCOUT_DEREGISTER_DEVICE_HANDLE: + case PMCOUT_GET_DEVICE_HANDLE: + case PMCOUT_SATA_COMPLETION: + case PMCOUT_SSP_ABORT: + case PMCOUT_SATA_ABORT: + case PMCOUT_SMP_ABORT: + case PMCOUT_SAS_HW_EVENT_ACK_ACK: { + uint32_t htag; + pmcwork_t *wp; + pmcs_phy_t *phy; + uintptr_t _wp, _phy; + uint16_t index; + + htag = LE_32(*(qentryp + 1)); + index = htag & PMCS_TAG_INDEX_MASK; + + wp = mdb_alloc(sizeof (pmcwork_t), UM_SLEEP); + _wp = (uintptr_t)ss.work + (sizeof (pmcwork_t) * index); + + if (MDB_RD(wp, sizeof (pmcwork_t), _wp) == -1) { + NOREAD(pmcwork_t, _wp); + mdb_free(wp, sizeof (pmcwork_t)); + break; + } + + phy = mdb_alloc(sizeof (pmcs_phy_t), UM_SLEEP); + if (wp->phy == NULL) { + _phy = (uintptr_t)wp->last_phy; + } else { + _phy = (uintptr_t)wp->phy; + } + + /* + * If we have a PHY, read it in and get it's handle + */ + if (_phy != NULL) { + if (MDB_RD(phy, sizeof (*phy), _phy) == -1) { + NOREAD(pmcs_phy_t, phy); + } else { + devid = phy->device_id; + } + } + + mdb_free(phy, sizeof (pmcs_phy_t)); + mdb_free(wp, sizeof (pmcwork_t)); + break; + } + + /* + * The device ID is in the outbound IOMB at word 1 + */ + case PMCOUT_SSP_REQUEST_RECEIVED: + devid = LE_32(*(qentryp + 1)) & PMCS_DEVICE_ID_MASK; + break; + + /* + * The device ID is in the outbound IOMB at word 2 + */ + case PMCOUT_DEVICE_HANDLE_ARRIVED: + case PMCOUT_DEVICE_HANDLE_REMOVED: + devid = LE_32(*(qentryp + 2)) & PMCS_DEVICE_ID_MASK; + break; + + /* + * In this (very rare - never seen it) state, the device ID + * comes from the HTAG in the inbound IOMB, which would be word + * 3 in the outbound IOMB + */ + case PMCOUT_GENERAL_EVENT: + /* + * The device ID is in the outbound IOMB at word 3 + */ + case PMCOUT_DEVICE_REGISTRATION: + case PMCOUT_DEVICE_INFO: + case PMCOUT_SET_DEVICE_STATE: + case PMCOUT_GET_DEVICE_STATE: + case PMCOUT_SET_DEVICE_INFO: + devid = LE_32(*(qentryp + 3)) & PMCS_DEVICE_ID_MASK; + break; + + /* + * Device ID is in the outbound IOMB at word 4 + */ + case PMCOUT_SATA_EVENT: + case PMCOUT_SSP_EVENT: + devid = LE_32(*(qentryp + 4)) & PMCS_DEVICE_ID_MASK; + break; + } + + return (devid); +} + +static boolean_t +iomb_is_dev_hdl_specific(uint32_t word0, boolean_t inbound) +{ + uint16_t opcode = word0 & PMCS_IOMB_OPCODE_MASK; + + if (inbound) { + switch (opcode) { + case PMCIN_SSP_INI_IO_START: + case PMCIN_SSP_INI_TM_START: + case PMCIN_SSP_INI_EXT_IO_START: + case PMCIN_SSP_TGT_IO_START: + case PMCIN_SSP_TGT_RESPONSE_START: + case PMCIN_SSP_ABORT: + case PMCIN_DEREGISTER_DEVICE_HANDLE: + case PMCIN_SMP_REQUEST: + case PMCIN_SMP_RESPONSE: + case PMCIN_SMP_ABORT: + case PMCIN_ASSISTED_DISCOVERY: + case PMCIN_SATA_HOST_IO_START: + case PMCIN_SATA_ABORT: + case PMCIN_GET_DEVICE_INFO: + case PMCIN_SET_DEVICE_STATE: + case PMCIN_GET_DEVICE_STATE: + return (B_TRUE); + } + + return (B_FALSE); + } + + switch (opcode) { + case PMCOUT_SSP_COMPLETION: + case PMCOUT_SMP_COMPLETION: + case PMCOUT_DEVICE_REGISTRATION: + case PMCOUT_DEREGISTER_DEVICE_HANDLE: + case PMCOUT_GET_DEVICE_HANDLE: + case PMCOUT_SATA_COMPLETION: + case PMCOUT_SATA_EVENT: + case PMCOUT_SSP_EVENT: + case PMCOUT_DEVICE_HANDLE_ARRIVED: + case PMCOUT_SSP_REQUEST_RECEIVED: + case PMCOUT_DEVICE_INFO: + case PMCOUT_FW_FLASH_UPDATE: + case PMCOUT_GENERAL_EVENT: + case PMCOUT_SSP_ABORT: + case PMCOUT_SATA_ABORT: + case PMCOUT_SAS_HW_EVENT_ACK_ACK: + case PMCOUT_SMP_ABORT: + case PMCOUT_DEVICE_HANDLE_REMOVED: + case PMCOUT_SET_DEVICE_STATE: + case PMCOUT_GET_DEVICE_STATE: + case PMCOUT_SET_DEVICE_INFO: + return (B_TRUE); + } + + return (B_FALSE); +} + static void -dump_one_qentry_outbound(uint32_t *qentryp, int idx) +dump_one_qentry_outbound(struct pmcs_hw ss, uint32_t *qentryp, int idx, + uint64_t devid_filter) { int qeidx; uint32_t word0 = LE_32(*qentryp); uint32_t word1 = LE_32(*(qentryp + 1)); uint8_t iop_event; + uint32_t devid; + + /* + * Check to see if we're filtering on a device ID + */ + if (devid_filter != PMCS_INVALID_DEVICE_ID) { + if (!iomb_is_dev_hdl_specific(word0, B_FALSE)) { + return; + } + + /* + * Go find the device id. It might be in the outbound + * IOMB or we may have to go find the work structure and + * get it from there. + */ + devid = get_devid_from_ob_iomb(ss, qentryp, + word0 & PMCS_IOMB_OPCODE_MASK); + if ((devid == PMCS_INVALID_DEVICE_ID) || + (devid_filter != devid)) { + return; + } + } mdb_printf("Entry #%02d\n", idx); mdb_inc_indent(2); @@ -1652,7 +1832,8 @@ dump_one_qentry_outbound(uint32_t *qentryp, int idx) } static void -display_outbound_queues(struct pmcs_hw ss, uint_t verbose) +display_outbound_queues(struct pmcs_hw ss, uint64_t devid_filter, + uint_t verbose) { int idx, qidx; uintptr_t obqp; @@ -1709,7 +1890,8 @@ display_outbound_queues(struct pmcs_hw ss, uint_t verbose) last_consumed))); break; } - dump_one_qentry_outbound(qentryp, last_consumed); + dump_one_qentry_outbound(ss, qentryp, last_consumed, + devid_filter); mdb_printf("\n"); mdb_dec_indent(2); continue; @@ -1722,7 +1904,8 @@ display_outbound_queues(struct pmcs_hw ss, uint_t verbose) (obqp + (PMCS_QENTRY_SIZE * idx))); break; } - dump_one_qentry_outbound(qentryp, idx); + dump_one_qentry_outbound(ss, qentryp, idx, + devid_filter); } mdb_printf("\n"); @@ -1734,10 +1917,24 @@ display_outbound_queues(struct pmcs_hw ss, uint_t verbose) } static void -dump_one_qentry_inbound(uint32_t *qentryp, int idx) +dump_one_qentry_inbound(uint32_t *qentryp, int idx, uint64_t devid_filter) { int qeidx; uint32_t word0 = LE_32(*qentryp); + uint32_t devid = LE_32(*(qentryp + 2)); + + /* + * Check to see if we're filtering on a device ID + */ + if (devid_filter != PMCS_INVALID_DEVICE_ID) { + if (iomb_is_dev_hdl_specific(word0, B_TRUE)) { + if (devid_filter != devid) { + return; + } + } else { + return; + } + } mdb_printf("Entry #%02d\n", idx); mdb_inc_indent(2); @@ -1769,7 +1966,7 @@ dump_one_qentry_inbound(uint32_t *qentryp, int idx) } static void -display_inbound_queues(struct pmcs_hw ss, uint_t verbose) +display_inbound_queues(struct pmcs_hw ss, uint64_t devid_filter, uint_t verbose) { int idx, qidx, iqci, last_consumed; uintptr_t ibqp; @@ -1821,7 +2018,8 @@ display_inbound_queues(struct pmcs_hw ss, uint_t verbose) last_consumed))); break; } - dump_one_qentry_inbound(qentryp, last_consumed); + dump_one_qentry_inbound(qentryp, last_consumed, + devid_filter); mdb_printf("\n"); mdb_dec_indent(2); continue; @@ -1834,7 +2032,7 @@ display_inbound_queues(struct pmcs_hw ss, uint_t verbose) (ibqp + (PMCS_QENTRY_SIZE * idx))); break; } - dump_one_qentry_inbound(qentryp, idx); + dump_one_qentry_inbound(qentryp, idx, devid_filter); } mdb_printf("\n"); @@ -2835,6 +3033,9 @@ pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) uint_t dtc_info = FALSE; uint_t wserno = FALSE; uint_t fwlog = FALSE; + boolean_t devid_filter = FALSE; + uintptr_t pdevid; + uint32_t devid; int rv = DCMD_OK; void *pmcs_state; char *state_str; @@ -2858,6 +3059,7 @@ pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) if (mdb_getopts(argc, argv, 'c', MDB_OPT_SETBITS, TRUE, &compq, 'd', MDB_OPT_SETBITS, TRUE, &dtc_info, + 'D', MDB_OPT_UINTPTR_SET, &devid_filter, &pdevid, 'e', MDB_OPT_SETBITS, TRUE, &fwlog, 'h', MDB_OPT_SETBITS, TRUE, &hw_info, 'i', MDB_OPT_SETBITS, TRUE, &ic_info, @@ -2885,6 +3087,23 @@ pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) iport_info = TRUE; } + /* + * The -D option is meaningless without -q and/or -Q, and implies + * verbosity. + */ + if (devid_filter) { + devid = (uint64_t)pdevid & 0xffffffff; + if (!ibq && !obq) { + mdb_printf("-D requires either -q or -Q\n"); + return (DCMD_USAGE); + } + if (devid > PMCS_DEVICE_ID_MASK) { + mdb_printf("Device ID invalid\n"); + return (DCMD_USAGE); + } + verbose = TRUE; + } + if (MDB_RD(&ss, sizeof (ss), addr) == -1) { NOREAD(pmcs_hw_t, addr); return (DCMD_ERR); @@ -2956,10 +3175,10 @@ pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) display_ic(ss, verbose); if (ibq) - display_inbound_queues(ss, verbose); + display_inbound_queues(ss, devid, verbose); if (obq) - display_outbound_queues(ss, verbose); + display_outbound_queues(ss, devid, verbose); if (iport_info) display_iport(ss, addr, verbose, &pis); @@ -2984,6 +3203,7 @@ pmcs_help() mdb_printf("Prints summary information about each pmcs instance.\n" " -c: Dump the completion queue\n" " -d: Print per-iport information about device tree children\n" + " -D <device ID>: With -q/-Q, filter by device handle\n" " -e: Display the in-memory firmware event log\n" " -h: Print more detailed hardware information\n" " -i: Print interrupt coalescing information\n" @@ -3023,8 +3243,8 @@ pmcs_tag_help() } static const mdb_dcmd_t dcmds[] = { - { "pmcs", "?[-cdehiImpQqtTuwWv]", "print pmcs information", - pmcs_dcmd, pmcs_help + { "pmcs", "?[-cdehiImpQqtTuwWv] [-D <device ID>]", + "print pmcs information", pmcs_dcmd, pmcs_help }, { "pmcs_log", "?[-v] [-p PHY_PATH | -s SAS_ADDRESS | -l TAIL_LINES]", diff --git a/usr/src/cmd/mdb/common/modules/sctp/sctp.c b/usr/src/cmd/mdb/common/modules/sctp/sctp.c index b83ccc4b82..5901efcab8 100644 --- a/usr/src/cmd/mdb/common/modules/sctp/sctp.c +++ b/usr/src/cmd/mdb/common/modules/sctp/sctp.c @@ -144,30 +144,31 @@ sctp_faddr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) return (DCMD_ERR); } - statestr = sctp_faddr_state(fa->state); - mdb_printf("%<u>%p\t%<b>%N%</b>\t%s%</u>\n", addr, &fa->faddr, + statestr = sctp_faddr_state(fa->sf_state); + mdb_printf("%<u>%p\t%<b>%N%</b>\t%s%</u>\n", addr, &fa->sf_faddr, statestr); - mdb_printf("next\t\t%?p\tsaddr\t%N\n", fa->next, &fa->saddr); - mdb_printf("rto\t\t%?d\tsrtt\t\t%?d\n", fa->rto, fa->srtt); - mdb_printf("rttvar\t\t%?d\trtt_updates\t%?u\n", fa->rttvar, - fa->rtt_updates); - mdb_printf("strikes\t\t%?d\tmax_retr\t%?d\n", fa->strikes, - fa->max_retr); - mdb_printf("hb_expiry\t%?ld\thb_interval\t%?u\n", fa->hb_expiry, - fa->hb_interval); - mdb_printf("pmss\t\t%?u\tcwnd\t\t%?u\n", fa->sfa_pmss, fa->cwnd); - mdb_printf("ssthresh\t%?u\tsuna\t\t%?u\n", fa->ssthresh, fa->suna); - mdb_printf("pba\t\t%?u\tacked\t\t%?u\n", fa->pba, fa->acked); - mdb_printf("lastactive\t%?ld\thb_secret\t%?#lx\n", fa->lastactive, - fa->hb_secret); - mdb_printf("rxt_unacked\t%?u\n", fa->rxt_unacked); - mdb_printf("timer_mp\t%?p\tixa\t\t%?p\n", fa->timer_mp, fa->ixa); + mdb_printf("next\t\t%?p\tsaddr\t%N\n", fa->sf_next, &fa->sf_saddr); + mdb_printf("rto\t\t%?d\tsrtt\t\t%?d\n", fa->sf_rto, fa->sf_srtt); + mdb_printf("rttvar\t\t%?d\trtt_updates\t%?u\n", fa->sf_rttvar, + fa->sf_rtt_updates); + mdb_printf("strikes\t\t%?d\tmax_retr\t%?d\n", fa->sf_strikes, + fa->sf_max_retr); + mdb_printf("hb_expiry\t%?ld\thb_interval\t%?u\n", fa->sf_hb_expiry, + fa->sf_hb_interval); + mdb_printf("pmss\t\t%?u\tcwnd\t\t%?u\n", fa->sf_pmss, fa->sf_cwnd); + mdb_printf("ssthresh\t%?u\tsuna\t\t%?u\n", fa->sf_ssthresh, + fa->sf_suna); + mdb_printf("pba\t\t%?u\tacked\t\t%?u\n", fa->sf_pba, fa->sf_acked); + mdb_printf("lastactive\t%?ld\thb_secret\t%?#lx\n", fa->sf_lastactive, + fa->sf_hb_secret); + mdb_printf("rxt_unacked\t%?u\n", fa->sf_rxt_unacked); + mdb_printf("timer_mp\t%?p\tixa\t\t%?p\n", fa->sf_timer_mp, fa->sf_ixa); mdb_printf("hb_enabled\t%?d\thb_pending\t%?d\n" "timer_running\t%?d\tdf\t\t%?d\n" "pmtu_discovered\t%?d\tisv4\t\t%?d\n" "retransmissions\t%?u\n", - fa->hb_enabled, fa->hb_pending, fa->timer_running, fa->df, - fa->pmtu_discovered, fa->isv4, fa->T3expire); + fa->sf_hb_enabled, fa->sf_hb_pending, fa->sf_timer_running, + fa->sf_df, fa->sf_pmtu_discovered, fa->sf_isv4, fa->sf_T3expire); return (DCMD_OK); } @@ -453,8 +454,8 @@ sctp_reass_list(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av) "\t\tprev: %?p\tcont: %?p\n", addr, srpmp.b_next, srpmp.b_prev, srpmp.b_cont); mdb_printf("\t\tssn: %hu\tneeded: %hu\tgot: %hu\ttail: %?p\n" - "\t\tpartial_delivered: %s\n", srp.ssn, srp.needed, - srp.got, srp.tail, srp.partial_delivered ? "TRUE" : + "\t\tpartial_delivered: %s\n", srp.sr_ssn, srp.sr_needed, + srp.sr_got, srp.sr_tail, srp.sr_partial_delivered ? "TRUE" : "FALSE"); /* display the contents of this ssn's reassemby list */ @@ -671,9 +672,9 @@ print_faddr(uintptr_t ptr, const void *addr, void *cbdata) sctp_faddr_t *faddr = (sctp_faddr_t *)addr; int *i = cbdata; - statestr = sctp_faddr_state(faddr->state); + statestr = sctp_faddr_state(faddr->sf_state); - mdb_printf("\t%d:\t%N\t%?p (%s)\n", (*i)++, &faddr->faddr, ptr, + mdb_printf("\t%d:\t%N\t%?p (%s)\n", (*i)++, &faddr->sf_faddr, ptr, statestr); return (WALK_NEXT); } @@ -732,7 +733,7 @@ sctp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) /* non-verbose faddrs, suitable for pipelines to sctp_faddr */ if (paddr != 0) { sctp_faddr_t faddr, *fp; - for (fp = sctp->sctp_faddrs; fp != NULL; fp = faddr.next) { + for (fp = sctp->sctp_faddrs; fp != NULL; fp = faddr.sf_next) { if (mdb_vread(&faddr, sizeof (faddr), (uintptr_t)fp) == -1) { mdb_warn("failed to read faddr at %p", @@ -754,7 +755,7 @@ sctp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) sctp_faddr_t faddr; if (mdb_vread(&faddr, sizeof (faddr), (uintptr_t)sctp->sctp_faddrs) != -1) - mdb_printf("%<u> %N%</u>", &faddr.faddr); + mdb_printf("%<u> %N%</u>", &faddr.sf_faddr); } mdb_printf("\n"); @@ -829,10 +830,10 @@ sctp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) mdb_printf("%<b>Flow Control%</b>\n"); mdb_printf("tconn_sndbuf\t%?d\n" "conn_sndlowat\t%?d\tfrwnd\t\t%?u\n" - "rwnd\t\t%?u\tinitial rwnd\t%?u\n" + "rwnd\t\t%?u\tlast advertised rwnd\t%?u\n" "rxqueued\t%?u\tcwnd_max\t%?u\n", connp->conn_sndbuf, connp->conn_sndlowat, sctp->sctp_frwnd, - sctp->sctp_rwnd, sctp->sctp_irwnd, sctp->sctp_rxqueued, + sctp->sctp_rwnd, sctp->sctp_arwnd, sctp->sctp_rxqueued, sctp->sctp_cwnd_max); } @@ -1229,7 +1230,7 @@ sctp_walk_faddr_step(mdb_walk_state_t *wsp) status = wsp->walk_callback(faddr_ptr, &sctp_faddr, wsp->walk_cbdata); if (status != WALK_NEXT) return (status); - if ((faddr_ptr = (uintptr_t)sctp_faddr.next) == NULL) { + if ((faddr_ptr = (uintptr_t)sctp_faddr.sf_next) == NULL) { return (WALK_DONE); } else { wsp->walk_addr = faddr_ptr; @@ -1499,6 +1500,68 @@ sctp_stack_ipif_walk_step(mdb_walk_state_t *wsp) wsp->walk_cbdata)); } +/* + * Initialization function for the per CPU SCTP stats counter walker of a given + * SCTP stack. + */ +int +sctps_sc_walk_init(mdb_walk_state_t *wsp) +{ + sctp_stack_t sctps; + + if (wsp->walk_addr == NULL) + return (WALK_ERR); + + if (mdb_vread(&sctps, sizeof (sctps), wsp->walk_addr) == -1) { + mdb_warn("failed to read sctp_stack_t at %p", wsp->walk_addr); + return (WALK_ERR); + } + if (sctps.sctps_sc_cnt == 0) + return (WALK_DONE); + + /* + * Store the sctp_stack_t pointer in walk_data. The stepping function + * used it to calculate if the end of the counter has reached. + */ + wsp->walk_data = (void *)wsp->walk_addr; + wsp->walk_addr = (uintptr_t)sctps.sctps_sc; + return (WALK_NEXT); +} + +/* + * Stepping function for the per CPU SCTP stats counterwalker. + */ +int +sctps_sc_walk_step(mdb_walk_state_t *wsp) +{ + int status; + sctp_stack_t sctps; + sctp_stats_cpu_t *stats; + char *next, *end; + + if (mdb_vread(&sctps, sizeof (sctps), (uintptr_t)wsp->walk_data) == + -1) { + mdb_warn("failed to read sctp_stack_t at %p", wsp->walk_addr); + return (WALK_ERR); + } + if (mdb_vread(&stats, sizeof (stats), wsp->walk_addr) == -1) { + mdb_warn("failed ot read sctp_stats_cpu_t at %p", + wsp->walk_addr); + return (WALK_ERR); + } + status = wsp->walk_callback((uintptr_t)stats, &stats, wsp->walk_cbdata); + if (status != WALK_NEXT) + return (status); + + next = (char *)wsp->walk_addr + sizeof (sctp_stats_cpu_t *); + end = (char *)sctps.sctps_sc + sctps.sctps_sc_cnt * + sizeof (sctp_stats_cpu_t *); + if (next >= end) + return (WALK_DONE); + wsp->walk_addr = (uintptr_t)next; + return (WALK_NEXT); +} + static void sctp_help(void) { @@ -1522,6 +1585,7 @@ sctp_help(void) mdb_printf("\t-d\t Local and Peer addresses\n"); mdb_printf("\t-P\t Peer addresses\n"); } + static const mdb_dcmd_t dcmds[] = { { "sctp", ":[-afhoimrSFHpRCcedP]", "display sctp control structure", sctp, sctp_help }, @@ -1591,8 +1655,8 @@ static const mdb_walker_t walkers[] = { sctp_stack_ill_walk_init, sctp_stack_ill_walk_step, NULL }, { "sctp_stack_walk_ipif", "walk the sctp_g_ipif list for one stack", sctp_stack_ipif_walk_init, sctp_stack_ipif_walk_step, NULL }, - { "sctp_stacks", "walk all the sctp_stack_t", - sctp_stacks_walk_init, sctp_stacks_walk_step, NULL }, + { "sctps_sc", "walk all the per CPU stats counters of a sctp_stack_t", + sctps_sc_walk_init, sctps_sc_walk_step, NULL }, { NULL } }; diff --git a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c index 58ebaa2945..ebb843c193 100644 --- a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c +++ b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c @@ -375,6 +375,8 @@ static int smb_node(uintptr_t, uint_t, int, const mdb_arg_t *); static int smb_node_walk_init(mdb_walk_state_t *); static int smb_node_walk_step(mdb_walk_state_t *); static int smb_lock(uintptr_t, uint_t, int, const mdb_arg_t *); +static int smb_oplock(uintptr_t, uint_t, int, const mdb_arg_t *); +static int smb_oplock_grant(uintptr_t, uint_t, int, const mdb_arg_t *); static int smb_ace(uintptr_t, uint_t, int, const mdb_arg_t *); static int smb_ace_walk_init(mdb_walk_state_t *); static int smb_ace_walk_step(mdb_walk_state_t *); @@ -443,8 +445,12 @@ static const mdb_dcmd_t dcmds[] = { smb_dcmd_odir }, { "smbofile", "[-v]", - "print smb_odir_t information", + "print smb_file_t information", smb_dcmd_ofile }, + { "smboplock", NULL, + "print smb_oplock_t information", smb_oplock }, + { "smboplockgrant", NULL, + "print smb_oplock_grant_t information", smb_oplock_grant }, { "smbstat", NULL, "print all smb dispatched requests statistics", smb_stats }, @@ -799,7 +805,7 @@ static const char *smb_request_state[SMB_REQ_STATE_SENTINEL] = #define SMB_REQUEST_BANNER \ "%<b>%<u>%-?s %-?s %-14s %-14s %-16s %-32s%</u>%</b>\n" #define SMB_REQUEST_FORMAT \ - "%-?p %-?p %e %e %-16s %s\n" + "%-?p %-?p %-14lld %-14lld %-16s %s\n" static int smb_dcmd_request(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) @@ -866,10 +872,12 @@ smb_dcmd_request(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) "FID(file): %u (%p)\n" "PID: %u\n" "MID: %u\n\n" - "waiting time: %1.3e\n" - "running time: %1.3e", - sr->first_smb_com, smb_com[sr->first_smb_com], - sr->smb_com, smb_com[sr->smb_com], + "waiting time: %lld\n" + "running time: %lld\n", + sr->first_smb_com, + smb_com[sr->first_smb_com].smb_com, + sr->smb_com, + smb_com[sr->smb_com].smb_com, sr->sr_state, state, sr->smb_tid, sr->tid_tree, sr->smb_uid, sr->uid_user, @@ -897,7 +905,7 @@ smb_dcmd_request(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) waiting, running, state, - smb_com[sr->smb_com]); + smb_com[sr->smb_com].smb_com); } } return (DCMD_OK); @@ -1403,13 +1411,14 @@ static int smb_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { smb_node_t node; + int rc; int verbose = FALSE; int print_full_path = FALSE; int stack_trace = FALSE; vnode_t vnode; char od_name[MAXNAMELEN]; char path_name[1024]; - uintptr_t list_addr; + uintptr_t list_addr, oplock_addr; if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &verbose, @@ -1446,9 +1455,10 @@ smb_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) "%-18s " "%-6s " "%-6s " + "%-8s " "%-6s%</u>%</b>\n", "ADDR", "VP", "NODE-NAME", "OFILES", "LOCKS", - "REF"); + "OPLOCK", "REF"); } } @@ -1489,11 +1499,25 @@ smb_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) } (void) mdb_dec_indent(SMB_DCMD_INDENT); } + if (node.n_oplock.ol_count == 0) { + mdb_printf("Opportunistic Locks: 0\n"); + } else { + oplock_addr = + addr + offsetof(smb_node_t, n_oplock); + mdb_printf("Opportunistic Lock: %p\n", + oplock_addr); + rc = mdb_call_dcmd("smboplock", oplock_addr, + flags, argc, argv); + if (rc != DCMD_OK) + return (rc); + } mdb_printf("Reference Count: %u\n\n", node.n_refcnt); } else { - mdb_printf("%-?p %-?p %-18s %-6d %-6d %-6d\n", + mdb_printf("%-?p %-?p %-18s %-6d %-6d %-8d %-6d ", addr, node.vp, od_name, node.n_ofile_list.ll_count, - node.n_lock_list.ll_count, node.n_refcnt); + node.n_lock_list.ll_count, + node.n_oplock.ol_count, node.n_refcnt); + if (print_full_path) mdb_printf("\t%s\n", path_name); } @@ -1713,6 +1737,99 @@ smb_lock(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) } /* + * ***************************************************************************** + * ************************** smb_oplock_grant_t ******************************* + * ***************************************************************************** + */ +/*ARGSUSED*/ +static int +smb_oplock_grant(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + smb_oplock_grant_t grant; + char *level; + + if (!(flags & DCMD_ADDRSPEC)) + return (DCMD_USAGE); + + /* + * If this is the first invocation of the command, print a nice + * header line for the output that will follow. + */ + if (DCMD_HDRSPEC(flags)) { + mdb_printf("%<u>%-16s %-10s %-16s%</u>\n", + "Grants:", "LEVEL", "OFILE"); + } + + if (mdb_vread(&grant, sizeof (grant), addr) == sizeof (grant)) { + switch (grant.og_level) { + case SMB_OPLOCK_EXCLUSIVE: + level = "EXCLUSIVE"; + break; + case SMB_OPLOCK_BATCH: + level = "BATCH"; + break; + case SMB_OPLOCK_LEVEL_II: + level = "LEVEL_II"; + break; + default: + level = "UNKNOWN"; + break; + } + + mdb_printf("%-16p %-10s %-16p", addr, level, grant.og_ofile); + } + return (DCMD_OK); +} + +/* + * ***************************************************************************** + * ***************************** smb_oplock_t ********************************** + * ***************************************************************************** + */ +/*ARGSUSED*/ +static int +smb_oplock(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + smb_oplock_t oplock; + uintptr_t list_addr; + + if (!(flags & DCMD_ADDRSPEC)) + return (DCMD_USAGE); + + if (mdb_vread(&oplock, sizeof (oplock), addr) != sizeof (oplock)) { + mdb_warn("failed to read struct smb_oplock at %p", addr); + return (DCMD_ERR); + } + + if (oplock.ol_count == 0) + return (DCMD_OK); + + (void) mdb_inc_indent(SMB_DCMD_INDENT); + switch (oplock.ol_break) { + case SMB_OPLOCK_BREAK_TO_NONE: + mdb_printf("Break Pending: BREAK_TO_NONE\n"); + break; + case SMB_OPLOCK_BREAK_TO_LEVEL_II: + mdb_printf( + "Break Pending: BREAK_TO_LEVEL_II\n"); + break; + default: + break; + } + + list_addr = addr + offsetof(smb_oplock_t, ol_grants); + + if (mdb_pwalk_dcmd("list", "smboplockgrant", + argc, argv, list_addr)) { + mdb_warn("failed to walk oplock grants"); + } + + (void) mdb_dec_indent(SMB_DCMD_INDENT); + + return (DCMD_OK); +} + +/* * ::smbstat * * Prints SMB requests statistics. diff --git a/usr/src/cmd/mdb/common/modules/svc.startd/startd.c b/usr/src/cmd/mdb/common/modules/svc.startd/startd.c index 1f163db4a0..4fae0df7f1 100644 --- a/usr/src/cmd/mdb/common/modules/svc.startd/startd.c +++ b/usr/src/cmd/mdb/common/modules/svc.startd/startd.c @@ -19,12 +19,9 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <mdb/mdb_modapi.h> #include <libuutil.h> @@ -59,6 +56,14 @@ startd_status(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) u_longlong_t lookups; u_longlong_t dep_inserts, dep_cycle_ns, dep_insert_ns; size_t graph_num, restarter_num; + uint64_t ct_maint; + uint64_t ct_hwerr; + uint64_t ct_service; + uint64_t ct_global; + uint64_t ct_noprefs; + uint64_t ct_from_uninit; + uint64_t ct_bad_state; + uint64_t ct_ovr_prefs; if (mdb_readvar(&lookups, "dictionary_lookups") == -1) { mdb_warn("failed to read 'dictionary_lookups' value\n"); @@ -109,18 +114,71 @@ startd_status(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) return (DCMD_ERR); } + if (mdb_readvar(&ct_maint, "stev_ct_maint") == -1) { + mdb_warn("failed to read 'stev_ct_maint'\n"); + return (DCMD_ERR); + } + + if (mdb_readvar(&ct_hwerr, "stev_ct_hwerr") == -1) { + mdb_warn("failed to read 'stev_ct_hwerr'\n"); + return (DCMD_ERR); + } + + if (mdb_readvar(&ct_service, "stev_ct_service") == -1) { + mdb_warn("failed to read 'stev_ct_service'\n"); + return (DCMD_ERR); + } + + if (mdb_readvar(&ct_global, "stev_ct_global") == -1) { + mdb_warn("failed to read 'stev_ct_global'\n"); + return (DCMD_ERR); + } + + if (mdb_readvar(&ct_noprefs, "stev_ct_noprefs") == -1) { + mdb_warn("failed to read 'stev_ct_noprefs'\n"); + return (DCMD_ERR); + } + + if (mdb_readvar(&ct_from_uninit, "stev_ct_from_uninit") == -1) { + mdb_warn("failed to read 'stev_ct_from_uninit'\n"); + return (DCMD_ERR); + } + + if (mdb_readvar(&ct_bad_state, "stev_ct_bad_state") == -1) { + mdb_warn("failed to read 'stev_ct_bad_state'\n"); + return (DCMD_ERR); + } + + if (mdb_readvar(&ct_ovr_prefs, "stev_ct_ovr_prefs") == -1) { + mdb_warn("failed to read 'stev_ct_ovr_prefs'\n"); + return (DCMD_ERR); + } + mdb_printf( - " dictionary lookups: %llu\n" - " average lookup time: %llu us\n" - "graph dependency insertions: %llu\n" - " average cycle-check time: %llu us\n" - " avg dependency insert time: %llu us\n" - "number of nodes in dgraph: %llu\n" - "number of nodes in instance_list: %llu\n", lookups, + "General stats\n" + " dictionary lookups: %llu\n" + " average lookup time: %llu us\n" + " graph dependency insertions: %llu\n" + " average cycle-check time: %llu us\n" + " avg dependency insert time: %llu us\n" + " number of nodes in dgraph: %llu\n" + "number of nodes in instance_list: %llu\n" + "\nState Transition Events\n" + " maintenance: %llu\n" + " hardware error: %llu\n" + " service specific pref: %llu\n" + " system wide pref: %llu\n" + " no prefs, not raised: %llu\n" + " from unint, not raised: %llu\n" + " bad state, not raised: %llu\n" + " override pref, raised: %llu\n", lookups, lookups ? ns_total / (1000 * lookups) : 0, dep_inserts, dep_inserts ? dep_cycle_ns / (1000 * dep_inserts) : 0, dep_inserts ? dep_insert_ns / (1000 * dep_inserts) : 0, - (u_longlong_t)graph_num, (u_longlong_t)restarter_num); + (u_longlong_t)graph_num, (u_longlong_t)restarter_num, + ct_maint, ct_hwerr, ct_service, ct_global, ct_noprefs, + ct_from_uninit, ct_bad_state, ct_ovr_prefs); + return (DCMD_OK); } |
