diff options
Diffstat (limited to 'usr/src/cmd/mdb')
-rw-r--r-- | usr/src/cmd/mdb/Makefile.module | 4 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/mdb/mdb_modapi.c | 17 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/mdb/mdb_modapi.h | 5 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/conf/mapfile-extern | 7 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/Makefile.files | 2 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/avl.c | 27 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/avl.h | 6 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/gcore.c | 2420 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/gcore.h | 29 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/genunix.c | 11 | ||||
-rw-r--r-- | usr/src/cmd/mdb/intel/amd64/genunix/Makefile | 6 | ||||
-rw-r--r-- | usr/src/cmd/mdb/intel/ia32/genunix/Makefile | 6 | ||||
-rw-r--r-- | usr/src/cmd/mdb/intel/ia32/libpython2.6/Makefile | 2 |
13 files changed, 2532 insertions, 10 deletions
diff --git a/usr/src/cmd/mdb/Makefile.module b/usr/src/cmd/mdb/Makefile.module index c7d0fd23db..a5c6669c74 100644 --- a/usr/src/cmd/mdb/Makefile.module +++ b/usr/src/cmd/mdb/Makefile.module @@ -22,6 +22,8 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2013 by Delphix. All rights reserved. +# .KEEP_STATE: .SUFFIXES: @@ -93,6 +95,8 @@ LINTFILES = $(LINTFILES_$(MDBTGT)) kvm_TGTFLAGS = -D_KERNEL proc_TGTFLAGS = -D_USER +C99MODE = $(C99_ENABLE) + CFLAGS += $(CCVERBOSE) CFLAGS64 += $(CCVERBOSE) CPPFLAGS += $($(MDBTGT)_TGTFLAGS) -I../../../common diff --git a/usr/src/cmd/mdb/common/mdb/mdb_modapi.c b/usr/src/cmd/mdb/common/mdb/mdb_modapi.c index a675398302..a95a866158 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.c @@ -71,6 +71,23 @@ mdb_vwrite(const void *buf, size_t nbytes, uintptr_t addr) } ssize_t +mdb_aread(void *buf, size_t nbytes, uintptr_t addr, void *as) +{ + ssize_t rbytes = mdb_tgt_aread(mdb.m_target, as, buf, nbytes, addr); + + if (rbytes > 0 && rbytes < nbytes) + return (set_errbytes(rbytes, nbytes)); + + return (rbytes); +} + +ssize_t +mdb_awrite(const void *buf, size_t nbytes, uintptr_t addr, void *as) +{ + return (mdb_tgt_awrite(mdb.m_target, as, buf, nbytes, addr)); +} + +ssize_t mdb_fread(void *buf, size_t nbytes, uintptr_t addr) { ssize_t rbytes = mdb_tgt_fread(mdb.m_target, buf, nbytes, addr); diff --git a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h index bb880f9b11..c6b4956b3b 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h +++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h @@ -21,7 +21,7 @@ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright (c) 2013 Joyent, Inc. All rights reserved. */ @@ -218,6 +218,9 @@ extern int mdb_remove_walker(const char *); extern ssize_t mdb_vread(void *, size_t, uintptr_t); extern ssize_t mdb_vwrite(const void *, size_t, uintptr_t); +extern ssize_t mdb_aread(void *, size_t, uintptr_t, void *); +extern ssize_t mdb_awrite(const void *, size_t, uintptr_t, void *); + extern ssize_t mdb_fread(void *, size_t, uintptr_t); extern ssize_t mdb_fwrite(const void *, size_t, uintptr_t); diff --git a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern index 5b4b8f2be1..2491a7a8d5 100644 --- a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern +++ b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern @@ -1,6 +1,6 @@ # # Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2012 by Delphix. All rights reserved. +# Copyright (c) 2013 by Delphix. All rights reserved. # # CDDL HEADER START # @@ -45,13 +45,16 @@ SYMBOL_SCOPE { # Pmapping_iter { FLAGS = EXTERN }; _mdb_ks_ncpu { FLAGS = EXTERN }; + _mdb_ks_pagemask { FLAGS = EXTERN }; + _mdb_ks_pageoffset { FLAGS = EXTERN }; _mdb_ks_pageshift { FLAGS = EXTERN }; _mdb_ks_pagesize { FLAGS = EXTERN }; - _mdb_ks_pageoffset { FLAGS = EXTERN }; mdb { FLAGS = EXTERN }; mdb_add_walker { FLAGS = EXTERN }; mdb_alloc { FLAGS = EXTERN }; + mdb_aread { FLAGS = EXTERN }; + mdb_awrite { FLAGS = EXTERN }; mdb_call_dcmd { FLAGS = EXTERN }; mdb_callback_add { FLAGS = EXTERN }; mdb_callback_remove { FLAGS = EXTERN }; diff --git a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files index bbbf70e0b1..4270e3ff47 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files +++ b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files @@ -22,6 +22,7 @@ # Copyright 2011 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2013, Joyent, Inc. All rights reserved. +# Copyright (c) 2013 by Delphix. All rights reserved. # # @@ -46,6 +47,7 @@ GENUNIX_SRCS = \ findstack.c \ findstack_subr.c \ fm.c \ + gcore.c \ genunix.c \ group.c \ hotplug.c \ diff --git a/usr/src/cmd/mdb/common/modules/genunix/avl.c b/usr/src/cmd/mdb/common/modules/genunix/avl.c index 444af78fa1..d5def7b604 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/avl.c +++ b/usr/src/cmd/mdb/common/modules/genunix/avl.c @@ -22,8 +22,9 @@ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + */ #include <sys/avl.h> @@ -283,3 +284,25 @@ avl_walk_fini(mdb_walk_state_t *wsp) mdb_free(aw, sizeof (struct aw_info)); } + +/* + * This function is named avl_walk_mdb to avoid a naming conflict with the + * existing avl_walk function. + */ +int +avl_walk_mdb(uintptr_t addr, mdb_walk_cb_t callback, void *cbdata) +{ + mdb_walk_state_t ws; + int ret; + + ws.walk_addr = addr; + ws.walk_callback = callback; + ws.walk_cbdata = cbdata; + + avl_walk_init(&ws); + while ((ret = avl_walk_step(&ws)) == WALK_NEXT) + continue; + avl_walk_fini(&ws); + + return (ret); +} diff --git a/usr/src/cmd/mdb/common/modules/genunix/avl.h b/usr/src/cmd/mdb/common/modules/genunix/avl.h index 6f3bc202b7..d7e18328fb 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/avl.h +++ b/usr/src/cmd/mdb/common/modules/genunix/avl.h @@ -22,12 +22,13 @@ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + */ #ifndef _MDB_AVL_H #define _MDB_AVL_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -47,6 +48,7 @@ extern int avl_walk_init_range(mdb_walk_state_t *wsp, uintptr_t, uintptr_t, int (*)(void *, uintptr_t, void *), void *); extern int avl_walk_step(mdb_walk_state_t *); extern void avl_walk_fini(mdb_walk_state_t *wsp); +extern int avl_walk_mdb(uintptr_t, mdb_walk_cb_t, void *); #ifdef __cplusplus } diff --git a/usr/src/cmd/mdb/common/modules/genunix/gcore.c b/usr/src/cmd/mdb/common/modules/genunix/gcore.c new file mode 100644 index 0000000000..12636444c8 --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/genunix/gcore.c @@ -0,0 +1,2420 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + */ + +/* + * This file implements the mdb ::gcore command. The command relies on the + * libproc Pgcore function to actually generate the core file but we provide + * our own ops vector to populate data required by Pgcore. The ops vector + * function implementations simulate the functionality implemented by procfs. + * The data provided by some of the ops vector functions is not complete + * (missing data is documented in function headers) but there is enough + * information to generate a core file that can be loaded into mdb. + * + * Currently only x86 is supported! + */ + +#ifndef _KMDB + +/* + * The kernel has its own definition of exit which has a different signature + * than the user space definition. This seems to be the standard way to deal + * with this. + */ +#define exit kern_exit + +#include <mdb/mdb_modapi.h> +#include <mdb/mdb_param.h> +#include <mdb/mdb_ks.h> +#include <mdb/mdb_ctf.h> +#include <mdb/mdb_debug.h> + +#include <sys/class.h> +#include <sys/cpuvar.h> +#include <sys/proc.h> +#include <sys/cred_impl.h> +#include <sys/lgrp.h> +#include <sys/pool.h> +#include <sys/project.h> +#include <sys/regset.h> +#include <sys/schedctl.h> +#include <sys/session.h> +#include <sys/syscall.h> +#include <sys/task.h> +#include <sys/var.h> +#include <sys/privregs.h> +#include <sys/psw.h> +#include <sys/fault.h> +#include <sys/procfs.h> +#include <sys/sysmacros.h> +#include <sys/wait.h> +#include <vm/seg.h> +#include <vm/vpage.h> +#include <fs/proc/prdata.h> + +#undef exit + +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include <libproc.h> + +#include "avl.h" + +#ifdef _LP64 +#define LSPAN(type) (P2ROUNDUP(sizeof (type), 16)) +#else +#define LSPAN(type) (P2ROUNDUP(sizeof (type), 8)) +#endif + +#define vpgtob(n) ((n) * sizeof (struct vpage)) + +/* Macros to invoke gcore seg operations */ +#define GSOP_INIT(_gs) (_gs)->gs_ops->gsop_init((_gs)) +#define GSOP_FINI(_gs) (_gs)->gs_ops->gsop_fini((_gs)) +#define GSOP_INCORE(_gs, _addr, _eaddr) \ + (_gs)->gs_ops->gsop_incore((_gs), (_addr), (_eaddr)) +#define GSOP_GETPROT(_gs, _addr) \ + (_gs)->gs_ops->gsop_getprot((_gs), (_addr)) +#define GSOP_GETOFFSET(_gs, _addr) \ + (_gs)->gs_ops->gsop_getoffset((_gs), (_addr)) +#define GSOP_GETTYPE(_gs, _addr) \ + (_gs)->gs_ops->gsop_gettype((_gs), (_addr)) +#define GSOP_NAME(_gs, _name, _size) \ + (_gs)->gs_ops->gsop_name((_gs), (_name), (_size)) +#define GSOP_NORESERVE(_gs) \ + (_gs)->gs_ops->gsop_noreserve((_gs)) + +#ifdef GCORE_DEBUG +#define dprintf(...) mdb_printf(__VA_ARGS__) +#else +#define dprintf(...) +#endif + +/* mdb versions of kernel structures used for ctf read calls */ +typedef struct mdb_proc { + uintptr_t p_as; + uintptr_t p_brkbase; + size_t p_brksize; + uintptr_t p_usrstack; + size_t p_stksize; + user_t p_user; + uintptr_t p_agenttp; + uintptr_t p_tlist; + uintptr_t p_zone; + uintptr_t p_ldt; + kcondvar_t p_holdlwps; + int p_lwpcnt; + uintptr_t p_lwpdir; + uint_t p_lwpdir_sz; + uintptr_t p_cred; + uint_t p_flag; + int p_zombcnt; + uintptr_t p_pidp; + pid_t p_ppid; + uintptr_t p_pgidp; + uintptr_t p_sessp; + uintptr_t p_task; + uintptr_t p_pool; + model_t p_model; + char p_wcode; + ushort_t p_ldtlimit; + uintptr_t p_exec; + uint_t p_proc_flag; + ushort_t p_pidflag; + k_sigset_t p_ignore; + k_sigset_t p_siginfo; + k_sigset_t p_sig; + k_sigset_t p_sigmask; + k_fltset_t p_fltmask; + int p_wdata; +} mdb_proc_t; + +typedef struct mdb_kthread { + ushort_t t_proc_flag; + uint_t t_state; + lwpchan_t t_lwpchan; + ushort_t t_whystop; + uint8_t t_dtrace_stop; + uintptr_t t_forw; + uintptr_t t_lwp; + id_t t_tid; + short t_sysnum; + pri_t t_pri; + time_t t_start; + id_t t_cid; + uintptr_t t_cpu; + int t_bind_pset; + short t_bind_cpu; + uintptr_t t_lpl; + ushort_t t_schedflag; + ushort_t t_whatstop; + k_sigset_t t_sig; + uintptr_t t_schedctl; + k_sigset_t t_hold; + hrtime_t t_stoptime; +} mdb_kthread_t; + +typedef struct mdb_seg { + uintptr_t s_base; + size_t s_size; + uintptr_t s_ops; + uintptr_t s_data; + uintptr_t s_as; +} mdb_seg_t; + +typedef struct mdb_as { + uintptr_t a_proc; +} mdb_as_t; + +typedef struct mdb_segvn_data { + uintptr_t vp; + uint64_t offset; + uint16_t flags; + uint8_t pageprot; + uint8_t prot; + uintptr_t amp; + struct vpage *vpage; + uint64_t anon_index; + uint8_t type; +} mdb_segvn_data_t; + +typedef struct mdb_vnode { + enum vtype v_type; + uintptr_t v_data; + uintptr_t v_op; + uintptr_t v_path; +} mdb_vnode_t; + +typedef struct mdb_znode { + uint64_t z_size; +} mdb_znode_t; + +typedef struct mdb_tmpnode { + vattr_t tn_attr; +} mdb_tmpnode_t; + +typedef struct mdb_vnodeops { + uintptr_t vnop_name; +} mdb_vnodeops_t; + +typedef struct mdb_shm_data { + uintptr_t shm_sptseg; +} mdb_shm_data_t; + +typedef struct mdb_watched_page { + uintptr_t wp_vaddr; + uint8_t wp_oprot; +} mdb_watched_page_t; + +typedef struct mdb_pid { + pid_t pid_id; +} mdb_pid_t; + +typedef struct mdb_sess { + uintptr_t s_sidp; +} mdb_sess_t; + +typedef struct mdb_task { + taskid_t tk_tkid; + uintptr_t tk_proj; +} mdb_task_t; + +typedef struct mdb_kproject { + projid_t kpj_id; +} mdb_kproject_t; + +typedef struct mdb_zone { + zoneid_t zone_id; + uintptr_t zone_name; +} mdb_zone_t; + +typedef struct mdb_sc_shared { + char sc_sigblock; +} mdb_sc_shared_t; + +typedef struct mdb_klwp { + uintptr_t lwp_regs; + struct pcb lwp_pcb; + uchar_t lwp_asleep; + uchar_t lwp_cursig; + uintptr_t lwp_curinfo; + k_siginfo_t lwp_siginfo; + stack_t lwp_sigaltstack; + uintptr_t lwp_oldcontext; + short lwp_badpriv; + uintptr_t lwp_ustack; + char lwp_eosys; +} mdb_klwp_t; + +typedef struct mdb_cpu { + processorid_t cpu_id; +} mdb_cpu_t; + +typedef struct mdb_lpl { + lgrp_id_t lpl_lgrpid; +} mdb_lpl_t; + +typedef struct mdb_sigqueue { + k_siginfo_t sq_info; +} mdb_sigqueue_t; + +typedef struct mdb_pool { + poolid_t pool_id; +} mdb_pool_t; + +typedef struct mdb_amp { + uintptr_t ahp; +} mdb_amp_t; + +typedef struct mdb_anon_hdr { + pgcnt_t size; + uintptr_t array_chunk; + int flags; +} mdb_anon_hdr_t; + +typedef struct mdb_anon { + uintptr_t an_vp; + anoff_t an_off; +} mdb_anon_t; + +/* Used to construct a linked list of prmap_ts */ +typedef struct prmap_node { + struct prmap_node *next; + prmap_t m; +} prmap_node_t; + +/* Fields common to psinfo_t and pstatus_t */ +typedef struct pcommon { + int pc_nlwp; + int pc_nzomb; + pid_t pc_pid; + pid_t pc_ppid; + pid_t pc_pgid; + pid_t pc_sid; + taskid_t pc_taskid; + projid_t pc_projid; + zoneid_t pc_zoneid; + char pc_dmodel; +} pcommon_t; + +/* AVL walk callback structures */ +typedef struct read_maps_cbarg { + mdb_proc_t *p; + uintptr_t brkseg; + uintptr_t stkseg; + prmap_node_t *map_head; + prmap_node_t *map_tail; + int map_len; +} read_maps_cbarg_t; + +typedef struct as_segat_cbarg { + uintptr_t addr; + uintptr_t res; +} as_segat_cbarg_t; + +typedef struct getwatchprot_cbarg { + uintptr_t wp_vaddr; + mdb_watched_page_t wp; + boolean_t found; +} getwatchprot_cbarg_t; + +struct gcore_segops; +typedef struct gcore_seg { + mdb_seg_t *gs_seg; + void *gs_data; + struct gcore_segops *gs_ops; +} gcore_seg_t; + +/* Callback function type for processing lwp entries */ +typedef int (*lwp_callback_t)(mdb_proc_t *, lwpent_t *, void *); + +/* Private data */ +static uintptr_t gcore_segvn_ops; +static priv_impl_info_t prinfo; +static sclass_t *gcore_sclass; +static uintptr_t gcore_kas; +static boolean_t gcore_initialized = B_FALSE; + +typedef int (*gsop_init_t)(gcore_seg_t *); +typedef void (*gsop_fini_t)(gcore_seg_t *); +typedef u_offset_t (*gsop_incore_t)(gcore_seg_t *, u_offset_t, u_offset_t); +typedef uint_t (*gsop_getprot_t)(gcore_seg_t *, u_offset_t); +typedef int (*gsop_getoffset_t)(gcore_seg_t *, u_offset_t); +typedef void (*gsop_name_t)(gcore_seg_t *, char *name, size_t size); +typedef int (*gsop_gettype_t)(gcore_seg_t *, u_offset_t); +typedef boolean_t (*gsop_noreserve_t)(gcore_seg_t *); + +typedef struct gcore_segops { + gsop_init_t gsop_init; + gsop_fini_t gsop_fini; + gsop_incore_t gsop_incore; + gsop_getprot_t gsop_getprot; + gsop_getoffset_t gsop_getoffset; + gsop_name_t gsop_name; + gsop_gettype_t gsop_gettype; + gsop_noreserve_t gsop_noreserve; +} gcore_segops_t; + +static void map_list_free(prmap_node_t *); +static uintptr_t gcore_prchoose(mdb_proc_t *); + +/* + * Segvn ops + */ +static int gsvn_init(gcore_seg_t *); +static void gsvn_fini(gcore_seg_t *); +static u_offset_t gsvn_incore(gcore_seg_t *, u_offset_t, u_offset_t); +static uint_t gsvn_getprot(gcore_seg_t *, u_offset_t); +static int gsvn_getoffset(gcore_seg_t *, u_offset_t); +static void gsvn_name(gcore_seg_t *, char *, size_t); +static int gsvn_gettype(gcore_seg_t *, u_offset_t); +static boolean_t gsvn_noreserve(gcore_seg_t *); + +static gcore_segops_t gsvn_ops = { + .gsop_init = gsvn_init, + .gsop_fini = gsvn_fini, + .gsop_incore = gsvn_incore, + .gsop_getprot = gsvn_getprot, + .gsop_getoffset = gsvn_getoffset, + .gsop_name = gsvn_name, + .gsop_gettype = gsvn_gettype, + .gsop_noreserve = gsvn_noreserve +}; + +static int +gsvn_init(gcore_seg_t *gs) +{ + mdb_seg_t *seg = gs->gs_seg; + mdb_segvn_data_t *svd = NULL; + struct vpage *vpage = NULL; + size_t nvpage = 0; + + if (seg->s_data != NULL) { + svd = mdb_alloc(sizeof (*svd), UM_SLEEP); + if (mdb_ctf_vread(svd, "segvn_data_t", "mdb_segvn_data_t", + seg->s_data, 0) == -1) { + goto error; + } + + if (svd->pageprot != 0) { + nvpage = seg_pages(seg); + dprintf("vpage count: %d\n", nvpage); + + vpage = mdb_alloc(vpgtob(nvpage), UM_SLEEP); + if (mdb_vread(vpage, vpgtob(nvpage), + (uintptr_t)svd->vpage) != vpgtob(nvpage)) { + mdb_warn("Failed to read vpages from %p\n", + svd->vpage); + goto error; + } + + svd->vpage = vpage; + } else { + svd->vpage = NULL; + } + gs->gs_data = svd; + } else { + gs->gs_data = NULL; + } + + return (0); + +error: + mdb_free(vpage, vpgtob(nvpage)); + mdb_free(svd, sizeof (*svd)); + return (-1); +} + +/*ARGSUSED*/ +static int +gsvn_getoffset(gcore_seg_t *gs, u_offset_t addr) +{ + mdb_segvn_data_t *svd = gs->gs_data; + mdb_seg_t *seg = gs->gs_seg; + + return (svd->offset + (uintptr_t)(addr - seg->s_base)); +} + +static void +gsvn_name(gcore_seg_t *gs, char *name, size_t size) +{ + mdb_segvn_data_t *svd = gs->gs_data; + + name[0] = '\0'; + if (svd->vp != 0) { + mdb_seg_t *seg = gs->gs_seg; + mdb_as_t as; + mdb_proc_t p; + mdb_vnode_t vn; + + if (mdb_ctf_vread(&vn, "vnode_t", "mdb_vnode_t", svd->vp, 0) + == -1) { + return; + } + + if (mdb_ctf_vread(&as, "struct as", "mdb_as_t", seg->s_as, 0) + == -1) { + return; + } + + if (mdb_ctf_vread(&p, "proc_t", "mdb_proc_t", as.a_proc, 0) + == -1) { + return; + } + + if (vn.v_type == VREG && svd->vp == p.p_exec) { + (void) strncpy(name, "a.out", size); + } + + /* + * procfs has more logic here to construct a name using + * vfs/vnode identifiers but didn't seem worthwhile to add + * here. + */ + } +} + +/*ARGSUSED*/ +static int +gsvn_gettype(gcore_seg_t *gs, u_offset_t addr) +{ + return (0); +} + +static void +gsvn_fini(gcore_seg_t *gs) +{ + mdb_segvn_data_t *svd = gs->gs_data; + + if (svd != NULL) { + if (svd->vpage != NULL) { + size_t nvpage = seg_pages(gs->gs_seg); + + mdb_free(svd->vpage, vpgtob(nvpage)); + } + mdb_free(svd, sizeof (*svd)); + } +} + +static boolean_t +gsvn_noreserve(gcore_seg_t *gs) +{ + mdb_segvn_data_t *svd = gs->gs_data; + + if (svd == NULL) { + return (B_FALSE); + } + + if (svd->flags & MAP_NORESERVE) { + mdb_vnode_t vn; + + if (svd->vp == 0) { + return (B_TRUE); + } + + if (mdb_ctf_vread(&vn, "vnode_t", "mdb_vnode_t", + svd->vp, 0) == -1) { + return (B_FALSE); + } + + if (vn.v_type != VREG) { + return (B_TRUE); + } + } + + return (B_FALSE); +} + +static uintptr_t +gcore_anon_get_ptr(uintptr_t ah_addr, ulong_t an_idx) +{ + mdb_anon_hdr_t ah; + uintptr_t anon_addr; + uintptr_t anon_ptr; + + if (mdb_ctf_vread(&ah, "struct anon_hdr", "mdb_anon_hdr_t", ah_addr, + 0) == -1) { + return (0); + } + + /* + * Single level case. + */ + if ((ah.size <= ANON_CHUNK_SIZE) || (ah.flags & ANON_ALLOC_FORCE)) { + anon_addr = ah.array_chunk + (sizeof (anon_ptr) * an_idx); + if (mdb_vread(&anon_ptr, sizeof (anon_ptr), anon_addr) != + sizeof (anon_ptr)) { + mdb_warn("Failed to read anon_ptr from %p (1 level)\n", + anon_addr); + return (0); + } + + return (anon_ptr & ANON_PTRMASK); + } + + /* + * 2 level case. + */ + anon_addr = ah.array_chunk + (sizeof (anon_ptr) * + (an_idx >> ANON_CHUNK_SHIFT)); + + if (mdb_vread(&anon_ptr, sizeof (anon_ptr), anon_addr) != + sizeof (anon_ptr)) { + mdb_warn("Failed to read anon_ptr from %p (2a level)\n", + anon_addr); + return (0); + } + + if (anon_ptr == 0) { + return (0); + } + + anon_addr = anon_ptr + (sizeof (anon_ptr) * + (an_idx & ANON_CHUNK_OFF)); + if (mdb_vread(&anon_ptr, sizeof (anon_ptr), anon_addr) != + sizeof (anon_ptr)) { + mdb_warn("Failed to read anon_ptr from %p (2b level)\n", + anon_addr); + return (0); + } + + return (anon_ptr & ANON_PTRMASK); +} + +static void +gcore_anon_get(uintptr_t ahp, ulong_t an_index, uintptr_t *vp, u_offset_t *off) +{ + mdb_anon_t anon; + uintptr_t ap; + + ap = gcore_anon_get_ptr(ahp, an_index); + if (ap != 0) { + if (mdb_ctf_vread(&anon, "struct anon", "mdb_anon_t", ap, 0) == + -1) { + return; + } + + *vp = anon.an_vp; + *off = anon.an_off; + } else { + *vp = 0; + *off = 0; + } +} + +static u_offset_t +gsvn_incore(gcore_seg_t *gs, u_offset_t addr, u_offset_t eaddr) +{ + mdb_segvn_data_t *svd = gs->gs_data; + mdb_seg_t *seg = gs->gs_seg; + mdb_amp_t amp; + u_offset_t offset; + uintptr_t vp; + size_t p, ep; + + if (svd->amp != 0 && mdb_ctf_vread(&, "amp_t", "mdb_amp_t", svd->amp, + 0) == -1) { + return (eaddr); + } + + p = seg_page(seg, addr); + ep = seg_page(seg, eaddr); + for (; p < ep; p++, addr += PAGESIZE) { + /* First check the anon map */ + if (svd->amp != 0) { + gcore_anon_get(amp.ahp, svd->anon_index + p, &vp, + &offset); + if (vp != 0 && mdb_page_lookup(vp, offset) != 0) { + break; + } + } + + /* Now check the segment's vnode */ + vp = svd->vp; + offset = svd->offset + (addr - gs->gs_seg->s_base); + if (mdb_page_lookup(vp, offset) != 0) { + break; + } + + dprintf("amp: %p vp: %p addr: %p offset: %p not in core!\n", + svd->amp, svd->vp, addr, offset); + } + + return (addr); +} + +static uint_t +gsvn_getprot(gcore_seg_t *gs, u_offset_t addr) +{ + mdb_segvn_data_t *svd = gs->gs_data; + mdb_seg_t *seg = gs->gs_seg; + + if (svd->pageprot == 0) { + return (svd->prot); + } + + dprintf("addr: %p pgno: %p\n", addr, seg_page(seg, addr)); + return (VPP_PROT(&svd->vpage[seg_page(seg, addr)])); +} + +/* + * Helper functions for constructing the process address space maps. + */ +/*ARGSUSED*/ +static int +as_segat_cb(uintptr_t seg_addr, const void *aw_buff, void *arg) +{ + as_segat_cbarg_t *as_segat_arg = arg; + mdb_seg_t seg; + + if (mdb_ctf_vread(&seg, "struct seg", "mdb_seg_t", seg_addr, 0) == -1) { + return (WALK_ERR); + } + + if (as_segat_arg->addr < seg.s_base) { + return (WALK_NEXT); + } + + if (as_segat_arg->addr >= seg.s_base + seg.s_size) { + return (WALK_NEXT); + } + + as_segat_arg->res = seg_addr; + return (WALK_DONE); +} + +/* + * Find a segment containing addr. + */ +static uintptr_t +gcore_as_segat(uintptr_t as_addr, uintptr_t addr) +{ + as_segat_cbarg_t as_segat_arg; + uintptr_t segtree_addr; + + as_segat_arg.addr = addr; + as_segat_arg.res = 0; + + segtree_addr = as_addr + mdb_ctf_offsetof_by_name("struct as", + "a_segtree"); + (void) avl_walk_mdb(segtree_addr, as_segat_cb, &as_segat_arg); + + return (as_segat_arg.res); +} + +static uintptr_t +gcore_break_seg(mdb_proc_t *p) +{ + uintptr_t addr = p->p_brkbase; + + if (p->p_brkbase != 0) + addr += p->p_brksize - 1; + + return (gcore_as_segat(p->p_as, addr)); +} + +/* ISA dependent function. */ +static uintptr_t +gcore_prgetstackbase(mdb_proc_t *p) +{ + return (p->p_usrstack - p->p_stksize); +} + +static u_offset_t +gcore_vnode_size(uintptr_t vnode_addr) +{ + mdb_vnode_t vnode; + mdb_vnodeops_t vnodeops; + char vops_name[128]; + + if (mdb_ctf_vread(&vnode, "vnode_t", "mdb_vnode_t", vnode_addr, 0) == + -1) { + return (-1); + } + + if (mdb_ctf_vread(&vnodeops, "vnodeops_t", "mdb_vnodeops_t", + vnode.v_op, 0) == -1) { + return (-1); + } + + if (mdb_readstr(vops_name, sizeof (vops_name), vnodeops.vnop_name) == + -1) { + mdb_warn("Failed to read vnop_name from %p\n", + vnodeops.vnop_name); + return (-1); + } + + if (strcmp(vops_name, "zfs") == 0) { + mdb_znode_t znode; + + if (mdb_ctf_vread(&znode, "znode_t", "mdb_znode_t", + vnode.v_data, 0) == -1) { + return (-1); + } + return (znode.z_size); + } + + if (strcmp(vops_name, "tmpfs") == 0) { + mdb_tmpnode_t tnode; + + if (mdb_ctf_vread(&tnode, "struct tmpnode", "mdb_tmpnode_t", + vnode.v_data, 0) == -1) { + return (-1); + } + return (tnode.tn_attr.va_size); + } + + /* Unknown file system type. */ + mdb_warn("Unknown fs type: %s\n", vops_name); + return (-1); +} + +static uint64_t +gcore_pr_getsegsize(mdb_seg_t *seg) +{ + uint64_t size = seg->s_size; + + if (seg->s_ops == gcore_segvn_ops) { + mdb_segvn_data_t svd; + + if (mdb_ctf_vread(&svd, "segvn_data_t", "mdb_segvn_data_t", + seg->s_data, 0) == -1) { + return (-1); + } + + if (svd.vp != 0) { + u_offset_t fsize; + u_offset_t offset; + + fsize = gcore_vnode_size(svd.vp); + if (fsize == -1) { + return (-1); + } + offset = svd.offset; + + if (fsize < offset) { + fsize = 0; + } else { + fsize -= offset; + } + + fsize = roundup(fsize, PAGESIZE); + } + + return (size); + } + + return (size); +} + +/*ARGSUSED*/ +static int +gcore_getwatchprot_cb(uintptr_t node_addr, const void *aw_buff, void *arg) +{ + getwatchprot_cbarg_t *cbarg = arg; + + if (mdb_ctf_vread(&cbarg->wp, "struct watched_page", + "mdb_watched_page_t", node_addr, 0) == -1) { + return (WALK_ERR); + } + + if (cbarg->wp.wp_vaddr == cbarg->wp_vaddr) { + cbarg->found = B_TRUE; + return (WALK_DONE); + } + + return (WALK_NEXT); +} + +static void +gcore_getwatchprot(uintptr_t as_addr, u_offset_t addr, uint_t *prot) +{ + getwatchprot_cbarg_t cbarg; + uintptr_t wp_addr; + + cbarg.wp_vaddr = (uintptr_t)addr & (uintptr_t)PAGEMASK; + cbarg.found = B_FALSE; + + wp_addr = as_addr + mdb_ctf_offsetof_by_name("struct as", "a_wpage"); + (void) avl_walk_mdb(wp_addr, gcore_getwatchprot_cb, &cbarg); + + if (cbarg.found) { + *prot = cbarg.wp.wp_oprot; + } +} + +static u_offset_t +gcore_pr_nextprot(gcore_seg_t *gs, u_offset_t *saddrp, u_offset_t eaddr, + uint_t *protp) +{ + uint_t prot, nprot; + u_offset_t addr = *saddrp; + uintptr_t as_addr = gs->gs_seg->s_as; + int noreserve = 0; + + noreserve = GSOP_NORESERVE(gs); + dprintf("addr: %p noreserve: %d\n", addr, noreserve); + + if (noreserve) { + addr = GSOP_INCORE(gs, addr, eaddr); + if (addr == eaddr) { + prot = 0; + *saddrp = addr; + goto out; + } + } + + prot = GSOP_GETPROT(gs, addr); + gcore_getwatchprot(as_addr, addr, &prot); + *saddrp = addr; + + for (addr += PAGESIZE; addr < eaddr; addr += PAGESIZE) { + /* Discontinuity */ + if (noreserve && GSOP_INCORE(gs, addr, eaddr) != addr) { + goto out; + } + + nprot = GSOP_GETPROT(gs, addr); + gcore_getwatchprot(as_addr, addr, &nprot); + + if (nprot != prot) { + break; + } + } + +out: + *protp = prot; + return (addr); +} + +/* + * Get the page protection for the given start address. + * - saddrp: in - start address + * out - contains address of first in core page + * - naddrp: out - address of next in core page that has different protection + * - eaddr: in - end address + */ +static uint_t +gcore_pr_getprot(gcore_seg_t *gs, u_offset_t *saddrp, u_offset_t *naddrp, + u_offset_t eaddr) +{ + u_offset_t naddr; + uint_t prot; + + dprintf("seg: %p saddr: %p eaddr: %p\n", + gs->gs_seg, *saddrp, eaddr); + + naddr = gcore_pr_nextprot(gs, saddrp, eaddr, &prot); + + dprintf("seg: %p saddr: %p naddr: %p eaddr: %p\n", + gs->gs_seg, *saddrp, naddr, eaddr); + + *naddrp = naddr; + return (prot); +} + +static gcore_seg_t * +gcore_seg_create(mdb_seg_t *seg) +{ + gcore_seg_t *gs; + + gs = mdb_alloc(sizeof (*gs), UM_SLEEP); + gs->gs_seg = seg; + if (seg->s_ops == gcore_segvn_ops) { + gs->gs_ops = &gsvn_ops; + } else { + mdb_warn("Unhandled segment type, ops: %p\n", seg->s_ops); + goto error; + } + + if (GSOP_INIT(gs) != 0) { + goto error; + } + + return (gs); + +error: + mdb_free(gs, sizeof (*gs)); + return (NULL); +} + +static void +gcore_seg_destroy(gcore_seg_t *gs) +{ + GSOP_FINI(gs); + mdb_free(gs, sizeof (*gs)); +} + +/*ARGSUSED*/ +static int +read_maps_cb(uintptr_t seg_addr, const void *aw_buff, void *arg) +{ + read_maps_cbarg_t *cbarg = arg; + mdb_segvn_data_t svd; + mdb_seg_t s; + mdb_seg_t *seg; + uint_t prot; + gcore_seg_t *gs; + uintptr_t eaddr; + u_offset_t saddr, baddr; + prmap_node_t *mnode; + prmap_t *mp; + + if (mdb_ctf_vread(&s, "struct seg", "mdb_seg_t", seg_addr, 0) == -1) { + return (WALK_ERR); + } + seg = &s; + eaddr = seg->s_base + gcore_pr_getsegsize(seg); + + if ((gs = gcore_seg_create(seg)) == NULL) { + mdb_warn("gcore_seg_create failed!\n"); + return (WALK_ERR); + } + + /* + * Iterate from the base of the segment to its end, allocating a new + * prmap_node at each address boundary (baddr) between ranges that + * have different virtual memory protections. + */ + for (saddr = seg->s_base; saddr < eaddr; saddr = baddr) { + prot = gcore_pr_getprot(gs, &saddr, &baddr, eaddr); + if (saddr == eaddr) { + break; + } + + mnode = mdb_alloc(sizeof (*mnode), UM_SLEEP); + mnode->next = NULL; + mp = &mnode->m; + + if (cbarg->map_head == NULL) { + cbarg->map_head = cbarg->map_tail = mnode; + } else { + cbarg->map_tail->next = mnode; + cbarg->map_tail = mnode; + } + cbarg->map_len++; + + mp->pr_vaddr = (uintptr_t)saddr; + mp->pr_size = baddr - saddr; + mp->pr_offset = GSOP_GETOFFSET(gs, saddr); + mp->pr_mflags = 0; + if (prot & PROT_READ) + mp->pr_mflags |= MA_READ; + if (prot & PROT_WRITE) + mp->pr_mflags |= MA_WRITE; + if (prot & PROT_EXEC) + mp->pr_mflags |= MA_EXEC; + if (GSOP_GETTYPE(gs, saddr) & MAP_SHARED) + mp->pr_mflags |= MA_SHARED; + if (GSOP_GETTYPE(gs, saddr) & MAP_NORESERVE) + mp->pr_mflags |= MA_NORESERVE; + if (seg->s_ops == gcore_segvn_ops) { + if (mdb_ctf_vread(&svd, "segvn_data_t", + "mdb_segvn_data_t", seg->s_data, 0) == 0 && + svd.vp == NULL) { + mp->pr_mflags |= MA_ANON; + } + } + if (seg_addr == cbarg->brkseg) + mp->pr_mflags |= MA_BREAK; + else if (seg_addr == cbarg->stkseg) + mp->pr_mflags |= MA_STACK; + + mp->pr_pagesize = PAGESIZE; + + /* + * Manufacture a filename for the "object" dir. + */ + GSOP_NAME(gs, mp->pr_mapname, sizeof (mp->pr_mapname)); + } + + gcore_seg_destroy(gs); + + return (0); +} + +/* + * Helper functions for retrieving process and lwp state. + */ +static int +pcommon_init(mdb_proc_t *p, pcommon_t *pc) +{ + mdb_pid_t pid; + mdb_sess_t sess; + mdb_task_t task; + mdb_kproject_t proj; + mdb_zone_t zone; + + pc->pc_nlwp = p->p_lwpcnt; + pc->pc_nzomb = p->p_zombcnt; + + if (mdb_ctf_vread(&pid, "struct pid", "mdb_pid_t", p->p_pidp, 0) == + -1) { + return (-1); + } + pc->pc_pid = pid.pid_id; + pc->pc_ppid = p->p_ppid; + + if (mdb_ctf_vread(&pid, "struct pid", "mdb_pid_t", p->p_pgidp, 0) == + -1) { + return (-1); + } + pc->pc_pgid = pid.pid_id; + + if (mdb_ctf_vread(&sess, "sess_t", "mdb_sess_t", p->p_sessp, 0) == + -1) { + return (-1); + } + if (mdb_ctf_vread(&pid, "struct pid", "mdb_pid_t", sess.s_sidp, 0) == + -1) { + return (-1); + } + pc->pc_sid = pid.pid_id; + + if (mdb_ctf_vread(&task, "task_t", "mdb_task_t", p->p_task, 0) == -1) { + return (-1); + } + pc->pc_taskid = task.tk_tkid; + + if (mdb_ctf_vread(&proj, "kproject_t", "mdb_kproject_t", task.tk_proj, + 0) == -1) { + return (-1); + } + pc->pc_projid = proj.kpj_id; + + if (mdb_ctf_vread(&zone, "zone_t", "mdb_zone_t", p->p_zone, 0) == -1) { + return (-1); + } + pc->pc_zoneid = zone.zone_id; + + switch (p->p_model) { + case DATAMODEL_ILP32: + pc->pc_dmodel = PR_MODEL_ILP32; + break; + case DATAMODEL_LP64: + pc->pc_dmodel = PR_MODEL_LP64; + break; + } + + return (0); +} + +static uintptr_t +gcore_prchoose(mdb_proc_t *p) +{ + mdb_kthread_t kthr; + mdb_kthread_t *t = &kthr; + ushort_t t_istop_whystop = 0; + ushort_t t_istop_whatstop = 0; + uintptr_t t_addr = NULL; + uintptr_t t_onproc = NULL; // running on processor + uintptr_t t_run = NULL; // runnable, on disp queue + uintptr_t t_sleep = NULL; // sleeping + uintptr_t t_susp = NULL; // suspended stop + uintptr_t t_jstop = NULL; // jobcontrol stop, w/o directed stop + uintptr_t t_jdstop = NULL; // jobcontrol stop with directed stop + uintptr_t t_req = NULL; // requested stop + uintptr_t t_istop = NULL; // event-of-interest stop + uintptr_t t_dtrace = NULL; // DTrace stop + + /* + * If the agent lwp exists, it takes precedence over all others. + */ + if ((t_addr = p->p_agenttp) != NULL) { + return (t_addr); + } + + if ((t_addr = p->p_tlist) == NULL) /* start at the head of the list */ + return (t_addr); + do { /* for each lwp in the process */ + if (mdb_ctf_vread(&kthr, "kthread_t", "mdb_kthread_t", + t_addr, 0) == -1) { + return (0); + } + + if (VSTOPPED(t)) { /* virtually stopped */ + if (t_req == NULL) + t_req = t_addr; + continue; + } + + switch (t->t_state) { + default: + return (0); + case TS_SLEEP: + if (t_sleep == NULL) + t_sleep = t_addr; + break; + case TS_RUN: + case TS_WAIT: + if (t_run == NULL) + t_run = t_addr; + break; + case TS_ONPROC: + if (t_onproc == NULL) + t_onproc = t_addr; + break; + /* + * Threads in the zombie state have the lowest + * priority when selecting a representative lwp. + */ + case TS_ZOMB: + break; + case TS_STOPPED: + switch (t->t_whystop) { + case PR_SUSPENDED: + if (t_susp == NULL) + t_susp = t_addr; + break; + case PR_JOBCONTROL: + if (t->t_proc_flag & TP_PRSTOP) { + if (t_jdstop == NULL) + t_jdstop = t_addr; + } else { + if (t_jstop == NULL) + t_jstop = t_addr; + } + break; + case PR_REQUESTED: + if (t->t_dtrace_stop && t_dtrace == NULL) + t_dtrace = t_addr; + else if (t_req == NULL) + t_req = t_addr; + break; + case PR_SYSENTRY: + case PR_SYSEXIT: + case PR_SIGNALLED: + case PR_FAULTED: + /* + * Make an lwp calling exit() be the + * last lwp seen in the process. + */ + if (t_istop == NULL || + (t_istop_whystop == PR_SYSENTRY && + t_istop_whatstop == SYS_exit)) { + t_istop = t_addr; + t_istop_whystop = t->t_whystop; + t_istop_whatstop = t->t_whatstop; + } + break; + case PR_CHECKPOINT: /* can't happen? */ + break; + default: + return (0); + } + break; + } + } while ((t_addr = t->t_forw) != p->p_tlist); + + if (t_onproc) + t_addr = t_onproc; + else if (t_run) + t_addr = t_run; + else if (t_sleep) + t_addr = t_sleep; + else if (t_jstop) + t_addr = t_jstop; + else if (t_jdstop) + t_addr = t_jdstop; + else if (t_istop) + t_addr = t_istop; + else if (t_dtrace) + t_addr = t_dtrace; + else if (t_req) + t_addr = t_req; + else if (t_susp) + t_addr = t_susp; + else /* TS_ZOMB */ + t_addr = p->p_tlist; + + return (t_addr); +} + +/* + * Fields not populated: + * - pr_stype + * - pr_oldpri + * - pr_nice + * - pr_time + * - pr_pctcpu + * - pr_cpu + */ +static int +gcore_prgetlwpsinfo(uintptr_t t_addr, mdb_kthread_t *t, lwpsinfo_t *psp) +{ + char c, state; + mdb_cpu_t cpu; + mdb_lpl_t lgrp; + uintptr_t str_addr; + + bzero(psp, sizeof (*psp)); + + psp->pr_flag = 0; /* lwpsinfo_t.pr_flag is deprecated */ + psp->pr_lwpid = t->t_tid; + psp->pr_addr = t_addr; + psp->pr_wchan = (uintptr_t)t->t_wchan; + + /* map the thread state enum into a process state enum */ + state = VSTOPPED(t) ? TS_STOPPED : t->t_state; + switch (state) { + case TS_SLEEP: state = SSLEEP; c = 'S'; break; + case TS_RUN: state = SRUN; c = 'R'; break; + case TS_ONPROC: state = SONPROC; c = 'O'; break; + case TS_ZOMB: state = SZOMB; c = 'Z'; break; + case TS_STOPPED: state = SSTOP; c = 'T'; break; + case TS_WAIT: state = SWAIT; c = 'W'; break; + default: state = 0; c = '?'; break; + } + psp->pr_state = state; + psp->pr_sname = c; + psp->pr_syscall = t->t_sysnum; + psp->pr_pri = t->t_pri; + psp->pr_start.tv_sec = t->t_start; + psp->pr_start.tv_nsec = 0L; + + str_addr = (uintptr_t)gcore_sclass[t->t_cid].cl_name; + if (mdb_readstr(psp->pr_clname, sizeof (psp->pr_clname) - 1, str_addr) + == -1) { + mdb_warn("Failed to read string from %p\n", str_addr); + return (-1); + } + bzero(psp->pr_name, sizeof (psp->pr_name)); + + if (mdb_ctf_vread(&cpu, "struct cpu", "mdb_cpu_t", t->t_cpu, 0) == -1) { + return (-1); + } + psp->pr_onpro = cpu.cpu_id; + psp->pr_bindpro = t->t_bind_cpu; + psp->pr_bindpset = t->t_bind_pset; + + if (mdb_ctf_vread(&lgrp, "lpl_t", "mdb_lpl_t", t->t_lpl, 0) == -1) { + return (-1); + } + psp->pr_lgrp = lgrp.lpl_lgrpid; + + return (0); +} + +/*ARGSUSED*/ +static int +gcore_lpsinfo_cb(mdb_proc_t *p, lwpent_t *lwent, void *data) +{ + lwpsinfo_t *lpsinfo = data; + uintptr_t t_addr = (uintptr_t)lwent->le_thread; + mdb_kthread_t kthrd; + + if (t_addr != 0) { + if (mdb_ctf_vread(&kthrd, "kthread_t", "mdb_kthread_t", t_addr, + 0) == -1) { + return (-1); + } + return (gcore_prgetlwpsinfo(t_addr, &kthrd, lpsinfo)); + } + + bzero(lpsinfo, sizeof (*lpsinfo)); + lpsinfo->pr_lwpid = lwent->le_lwpid; + lpsinfo->pr_state = SZOMB; + lpsinfo->pr_sname = 'Z'; + lpsinfo->pr_start.tv_sec = lwent->le_start; + lpsinfo->pr_bindpro = PBIND_NONE; + lpsinfo->pr_bindpset = PS_NONE; + return (0); +} + +static void +gcore_schedctl_finish_sigblock(mdb_kthread_t *t) +{ + mdb_sc_shared_t td; + mdb_sc_shared_t *tdp; + + if (t->t_schedctl == NULL) { + return; + } + + if (mdb_ctf_vread(&td, "sc_shared_t", "mdb_sc_shared_t", t->t_schedctl, + 0) == -1) { + return; + } + tdp = &td; + + if (tdp->sc_sigblock) { + t->t_hold.__sigbits[0] = FILLSET0 & ~CANTMASK0; + t->t_hold.__sigbits[1] = FILLSET1 & ~CANTMASK1; + t->t_hold.__sigbits[2] = FILLSET2 & ~CANTMASK2; + tdp->sc_sigblock = 0; + } +} + +static void +gcore_prgetaction(mdb_proc_t *p, user_t *up, uint_t sig, struct sigaction *sp) +{ + int nsig = NSIG; + + bzero(sp, sizeof (*sp)); + + if (sig != 0 && (unsigned)sig < nsig) { + sp->sa_handler = up->u_signal[sig-1]; + prassignset(&sp->sa_mask, &up->u_sigmask[sig-1]); + if (sigismember(&up->u_sigonstack, sig)) + sp->sa_flags |= SA_ONSTACK; + if (sigismember(&up->u_sigresethand, sig)) + sp->sa_flags |= SA_RESETHAND; + if (sigismember(&up->u_sigrestart, sig)) + sp->sa_flags |= SA_RESTART; + if (sigismember(&p->p_siginfo, sig)) + sp->sa_flags |= SA_SIGINFO; + if (sigismember(&up->u_signodefer, sig)) + sp->sa_flags |= SA_NODEFER; + if (sig == SIGCLD) { + if (p->p_flag & SNOWAIT) + sp->sa_flags |= SA_NOCLDWAIT; + if ((p->p_flag & SJCTL) == 0) + sp->sa_flags |= SA_NOCLDSTOP; + } + } +} + +/* ISA dependent function. */ +static int +gcore_prfetchinstr(mdb_klwp_t *lwp, ulong_t *ip) +{ + *ip = (ulong_t)(instr_t)lwp->lwp_pcb.pcb_instr; + return (lwp->lwp_pcb.pcb_flags & INSTR_VALID); +} + +/* ISA dependent function. */ +static int +gcore_prisstep(mdb_klwp_t *lwp) +{ + return ((lwp->lwp_pcb.pcb_flags & + (NORMAL_STEP|WATCH_STEP|DEBUG_PENDING)) != 0); +} + +/* ISA dependent function. */ +static void +gcore_getgregs(mdb_klwp_t *lwp, gregset_t grp) +{ + struct regs rgs; + struct regs *rp; + + if (mdb_vread(&rgs, sizeof (rgs), lwp->lwp_regs) != sizeof (rgs)) { + mdb_warn("Failed to read regs from %p\n", lwp->lwp_regs); + return; + } + rp = &rgs; + +#if defined(__amd64) + struct pcb *pcb = &lwp->lwp_pcb; + + grp[REG_RDI] = rp->r_rdi; + grp[REG_RSI] = rp->r_rsi; + grp[REG_RDX] = rp->r_rdx; + grp[REG_RCX] = rp->r_rcx; + grp[REG_R8] = rp->r_r8; + grp[REG_R9] = rp->r_r9; + grp[REG_RAX] = rp->r_rax; + grp[REG_RBX] = rp->r_rbx; + grp[REG_RBP] = rp->r_rbp; + grp[REG_R10] = rp->r_r10; + grp[REG_R11] = rp->r_r11; + grp[REG_R12] = rp->r_r12; + grp[REG_R13] = rp->r_r13; + grp[REG_R14] = rp->r_r14; + grp[REG_R15] = rp->r_r15; + grp[REG_FSBASE] = pcb->pcb_fsbase; + grp[REG_GSBASE] = pcb->pcb_gsbase; + if (pcb->pcb_rupdate == 1) { + grp[REG_DS] = pcb->pcb_ds; + grp[REG_ES] = pcb->pcb_es; + grp[REG_FS] = pcb->pcb_fs; + grp[REG_GS] = pcb->pcb_gs; + } else { + grp[REG_DS] = rp->r_ds; + grp[REG_ES] = rp->r_es; + grp[REG_FS] = rp->r_fs; + grp[REG_GS] = rp->r_gs; + } + grp[REG_TRAPNO] = rp->r_trapno; + grp[REG_ERR] = rp->r_err; + grp[REG_RIP] = rp->r_rip; + grp[REG_CS] = rp->r_cs; + grp[REG_SS] = rp->r_ss; + grp[REG_RFL] = rp->r_rfl; + grp[REG_RSP] = rp->r_rsp; +#else + bcopy(&rp->r_gs, grp, sizeof (gregset_t)); +#endif +} + +/* ISA dependent functions. */ +static int +gcore_prgetrvals(mdb_klwp_t *lwp, long *rval1, long *rval2) +{ + struct regs *r = lwptoregs(lwp); + + if (r->r_ps & PS_C) + return (r->r_r0); + if (lwp->lwp_eosys == JUSTRETURN) { + *rval1 = 0; + *rval2 = 0; + } else { + *rval1 = r->r_r0; + *rval2 = r->r_r1; + } + return (0); +} + +static void +gcore_prgetprregs(mdb_klwp_t *lwp, prgregset_t prp) +{ + gcore_getgregs(lwp, prp); +} + +/* + * Field not populated: + * - pr_tstamp + * - pr_utime + * - pr_stime + * - pr_syscall + * - pr_syarg + * - pr_nsysarg + * - pr_fpreg + */ +/*ARGSUSED*/ +static int +gcore_prgetlwpstatus(mdb_proc_t *p, uintptr_t t_addr, mdb_kthread_t *t, + lwpstatus_t *sp, zone_t *zp) +{ + uintptr_t lwp_addr = ttolwp(t); + mdb_klwp_t lw; + mdb_klwp_t *lwp; + ulong_t instr; + int flags; + uintptr_t str_addr; + struct pid pid; + + if (mdb_ctf_vread(&lw, "klwp_t", "mdb_klwp_t", lwp_addr, 0) == -1) { + return (-1); + } + lwp = &lw; + + bzero(sp, sizeof (*sp)); + flags = 0L; + if (t->t_state == TS_STOPPED) { + flags |= PR_STOPPED; + if ((t->t_schedflag & TS_PSTART) == 0) + flags |= PR_ISTOP; + } else if (VSTOPPED(t)) { + flags |= PR_STOPPED|PR_ISTOP; + } + if (!(flags & PR_ISTOP) && (t->t_proc_flag & TP_PRSTOP)) + flags |= PR_DSTOP; + if (lwp->lwp_asleep) + flags |= PR_ASLEEP; + if (t_addr == p->p_agenttp) + flags |= PR_AGENT; + if (!(t->t_proc_flag & TP_TWAIT)) + flags |= PR_DETACH; + if (t->t_proc_flag & TP_DAEMON) + flags |= PR_DAEMON; + if (p->p_proc_flag & P_PR_FORK) + flags |= PR_FORK; + if (p->p_proc_flag & P_PR_RUNLCL) + flags |= PR_RLC; + if (p->p_proc_flag & P_PR_KILLCL) + flags |= PR_KLC; + if (p->p_proc_flag & P_PR_ASYNC) + flags |= PR_ASYNC; + if (p->p_proc_flag & P_PR_BPTADJ) + flags |= PR_BPTADJ; + if (p->p_proc_flag & P_PR_PTRACE) + flags |= PR_PTRACE; + if (p->p_flag & SMSACCT) + flags |= PR_MSACCT; + if (p->p_flag & SMSFORK) + flags |= PR_MSFORK; + if (p->p_flag & SVFWAIT) + flags |= PR_VFORKP; + + if (mdb_vread(&pid, sizeof (struct pid), p->p_pgidp) != sizeof (pid)) { + mdb_warn("Failed to read pid from %p\n", p->p_pgidp); + return (-1); + } + if (pid.pid_pgorphaned) + flags |= PR_ORPHAN; + if (p->p_pidflag & CLDNOSIGCHLD) + flags |= PR_NOSIGCHLD; + if (p->p_pidflag & CLDWAITPID) + flags |= PR_WAITPID; + sp->pr_flags = flags; + if (VSTOPPED(t)) { + sp->pr_why = PR_REQUESTED; + sp->pr_what = 0; + } else { + sp->pr_why = t->t_whystop; + sp->pr_what = t->t_whatstop; + } + sp->pr_lwpid = t->t_tid; + sp->pr_cursig = lwp->lwp_cursig; + prassignset(&sp->pr_lwppend, &t->t_sig); + gcore_schedctl_finish_sigblock(t); + prassignset(&sp->pr_lwphold, &t->t_hold); + if (t->t_whystop == PR_FAULTED) { + bcopy(&lwp->lwp_siginfo, + &sp->pr_info, sizeof (k_siginfo_t)); + } else if (lwp->lwp_curinfo) { + mdb_sigqueue_t sigq; + + if (mdb_ctf_vread(&sigq, "sigqueue_t", "mdb_sigqueue_t", + lwp->lwp_curinfo, 0) == -1) { + return (-1); + } + bcopy(&sigq.sq_info, &sp->pr_info, sizeof (k_siginfo_t)); + } + + sp->pr_altstack = lwp->lwp_sigaltstack; + gcore_prgetaction(p, PTOU(p), lwp->lwp_cursig, &sp->pr_action); + sp->pr_oldcontext = lwp->lwp_oldcontext; + sp->pr_ustack = lwp->lwp_ustack; + + str_addr = (uintptr_t)gcore_sclass[t->t_cid].cl_name; + if (mdb_readstr(sp->pr_clname, sizeof (sp->pr_clname) - 1, str_addr) == + -1) { + mdb_warn("Failed to read string from %p\n", str_addr); + return (-1); + } + + /* + * Fetch the current instruction, if not a system process. + * We don't attempt this unless the lwp is stopped. + */ + if ((p->p_flag & SSYS) || p->p_as == gcore_kas) + sp->pr_flags |= (PR_ISSYS|PR_PCINVAL); + else if (!(flags & PR_STOPPED)) + sp->pr_flags |= PR_PCINVAL; + else if (!gcore_prfetchinstr(lwp, &instr)) + sp->pr_flags |= PR_PCINVAL; + else + sp->pr_instr = instr; + + if (gcore_prisstep(lwp)) + sp->pr_flags |= PR_STEP; + gcore_prgetprregs(lwp, sp->pr_reg); + if ((t->t_state == TS_STOPPED && t->t_whystop == PR_SYSEXIT) || + (flags & PR_VFORKP)) { + user_t *up; + auxv_t *auxp; + int i; + + sp->pr_errno = gcore_prgetrvals(lwp, &sp->pr_rval1, + &sp->pr_rval2); + if (sp->pr_errno == 0) + sp->pr_errpriv = PRIV_NONE; + else + sp->pr_errpriv = lwp->lwp_badpriv; + + if (t->t_sysnum == SYS_execve) { + up = PTOU(p); + sp->pr_sysarg[0] = 0; + sp->pr_sysarg[1] = (uintptr_t)up->u_argv; + sp->pr_sysarg[2] = (uintptr_t)up->u_envp; + for (i = 0, auxp = up->u_auxv; + i < sizeof (up->u_auxv) / sizeof (up->u_auxv[0]); + i++, auxp++) { + if (auxp->a_type == AT_SUN_EXECNAME) { + sp->pr_sysarg[0] = + (uintptr_t)auxp->a_un.a_ptr; + break; + } + } + } + } + return (0); +} + +static int +gcore_lstatus_cb(mdb_proc_t *p, lwpent_t *lwent, void *data) +{ + lwpstatus_t *lstatus = data; + uintptr_t t_addr = (uintptr_t)lwent->le_thread; + mdb_kthread_t kthrd; + + if (t_addr == NULL) { + return (1); + } + + if (mdb_ctf_vread(&kthrd, "kthread_t", "mdb_kthread_t", t_addr, 0) + == -1) { + return (-1); + } + + return (gcore_prgetlwpstatus(p, t_addr, &kthrd, lstatus, NULL)); +} + +static prheader_t * +gcore_walk_lwps(mdb_proc_t *p, lwp_callback_t callback, int nlwp, + size_t ent_size) +{ + void *ent; + prheader_t *php; + lwpdir_t *ldp; + lwpdir_t ld; + lwpent_t lwent; + int status; + int i; + + php = calloc(1, sizeof (prheader_t) + nlwp * ent_size); + if (php == NULL) { + return (NULL); + } + php->pr_nent = nlwp; + php->pr_entsize = ent_size; + + ent = php + 1; + for (ldp = (lwpdir_t *)p->p_lwpdir, i = 0; i < p->p_lwpdir_sz; i++, + ldp++) { + if (mdb_vread(&ld, sizeof (ld), (uintptr_t)ldp) != + sizeof (ld)) { + mdb_warn("Failed to read lwpdir_t from %p\n", ldp); + goto error; + } + + if (ld.ld_entry == NULL) { + continue; + } + + if (mdb_vread(&lwent, sizeof (lwent), (uintptr_t)ld.ld_entry) != + sizeof (lwent)) { + mdb_warn("Failed to read lwpent_t from %p\n", + ld.ld_entry); + goto error; + } + + status = callback(p, &lwent, ent); + if (status == -1) { + dprintf("lwp callback %p returned -1\n", callback); + goto error; + } + if (status == 1) { + dprintf("lwp callback %p returned 1\n", callback); + continue; + } + + ent = (caddr_t)ent + ent_size; + } + + return (php); + +error: + free(php); + return (NULL); +} + +/* + * Misc helper functions. + */ +/* + * convert code/data pair into old style wait status + */ +static int +gcore_wstat(int code, int data) +{ + int stat = (data & 0377); + + switch (code) { + case CLD_EXITED: + stat <<= 8; + break; + case CLD_DUMPED: + stat |= WCOREFLG; + break; + case CLD_KILLED: + break; + case CLD_TRAPPED: + case CLD_STOPPED: + stat <<= 8; + stat |= WSTOPFLG; + break; + case CLD_CONTINUED: + stat = WCONTFLG; + break; + default: + mdb_warn("wstat: bad code %d\n", code); + } + return (stat); +} + +#if defined(__i386) || defined(__amd64) +static void +gcore_usd_to_ssd(user_desc_t *usd, struct ssd *ssd, selector_t sel) +{ + ssd->bo = USEGD_GETBASE(usd); + ssd->ls = USEGD_GETLIMIT(usd); + ssd->sel = sel; + + /* + * set type, dpl and present bits. + */ + ssd->acc1 = usd->usd_type; + ssd->acc1 |= usd->usd_dpl << 5; + ssd->acc1 |= usd->usd_p << (5 + 2); + + /* + * set avl, DB and granularity bits. + */ + ssd->acc2 = usd->usd_avl; + +#if defined(__amd64) + ssd->acc2 |= usd->usd_long << 1; +#else + ssd->acc2 |= usd->usd_reserved << 1; +#endif + + ssd->acc2 |= usd->usd_def32 << (1 + 1); + ssd->acc2 |= usd->usd_gran << (1 + 1 + 1); +} +#endif + +static priv_set_t * +gcore_priv_getset(cred_t *cr, int set) +{ + if ((CR_FLAGS(cr) & PRIV_AWARE) == 0) { + switch (set) { + case PRIV_EFFECTIVE: + return (&CR_OEPRIV(cr)); + case PRIV_PERMITTED: + return (&CR_OPPRIV(cr)); + } + } + return (&CR_PRIVS(cr)->crprivs[set]); +} + +static void +gcore_priv_getinfo(const cred_t *cr, void *buf) +{ + struct priv_info_uint *ii; + + ii = buf; + ii->val = CR_FLAGS(cr); + ii->info.priv_info_size = (uint32_t)sizeof (*ii); + ii->info.priv_info_type = PRIV_INFO_FLAGS; +} + +static void +map_list_free(prmap_node_t *n) +{ + prmap_node_t *next; + + while (n != NULL) { + next = n->next; + mdb_free(n, sizeof (*n)); + n = next; + } +} + +/* + * Ops vector functions for ::gcore. + */ +/*ARGSUSED*/ +static ssize_t +Pread_gcore(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr, + void *data) +{ + mdb_proc_t *p = data; + ssize_t ret; + + ret = mdb_aread(buf, n, addr, (void *)p->p_as); + if (ret != n) { + dprintf("%s: addr: %p len: %llx\n", __func__, addr, n); + (void) memset(buf, 0, n); + return (n); + } + + return (ret); +} + +/*ARGSUSED*/ +static ssize_t +Pwrite_gcore(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr, + void *data) +{ + dprintf("%s: addr: %p len: %llx\n", __func__, addr, n); + + return (-1); +} + +/*ARGSUSED*/ +static int +Pread_maps_gcore(struct ps_prochandle *P, prmap_t **Pmapp, ssize_t *nmapp, + void *data) +{ + mdb_proc_t *p = data; + read_maps_cbarg_t cbarg; + prmap_node_t *n; + prmap_t *pmap; + uintptr_t segtree_addr; + int error; + int i; + + cbarg.p = p; + cbarg.brkseg = gcore_break_seg(p); + cbarg.stkseg = gcore_as_segat(p->p_as, gcore_prgetstackbase(p)); + + (void) memset(&cbarg, 0, sizeof (cbarg)); + segtree_addr = p->p_as + mdb_ctf_offsetof_by_name("struct as", + "a_segtree"); + error = avl_walk_mdb(segtree_addr, read_maps_cb, &cbarg); + if (error != WALK_DONE) { + return (-1); + } + + /* Conver the linked list into an array */ + pmap = malloc(cbarg.map_len * sizeof (*pmap)); + if (pmap == NULL) { + map_list_free(cbarg.map_head); + return (-1); + } + + for (i = 0, n = cbarg.map_head; i < cbarg.map_len; i++, n = n->next) { + (void) memcpy(&pmap[i], &n->m, sizeof (prmap_t)); + } + map_list_free(cbarg.map_head); + + for (i = 0; i < cbarg.map_len; i++) { + dprintf("pr_vaddr: %p pr_size: %llx, pr_name: %s " + "pr_offset: %p pr_mflags: 0x%x\n", + pmap[i].pr_vaddr, pmap[i].pr_size, + pmap[i].pr_mapname, pmap[i].pr_offset, + pmap[i].pr_mflags); + } + + *Pmapp = pmap; + *nmapp = cbarg.map_len; + + return (0); +} + +/*ARGSUSED*/ +static void +Pread_aux_gcore(struct ps_prochandle *P, auxv_t **auxvp, int *nauxp, void *data) +{ + mdb_proc_t *p = data; + auxv_t *auxv; + int naux; + + naux = __KERN_NAUXV_IMPL; + auxv = calloc(naux + 1, sizeof (*auxv)); + if (auxv == NULL) { + *auxvp = NULL; + *nauxp = 0; + return; + } + + (void) memcpy(auxv, p->p_user.u_auxv, naux * sizeof (*auxv)); + + *auxvp = auxv; + *nauxp = naux; +} + +/*ARGSUSED*/ +static int +Pcred_gcore(struct ps_prochandle *P, prcred_t *prcp, int ngroups, void *data) +{ + mdb_proc_t *p = data; + cred_t cr; + credgrp_t crgrp; + int i; + + if (mdb_vread(&cr, sizeof (cr), p->p_cred) != sizeof (cr)) { + mdb_warn("Failed to read cred_t from %p\n", p->p_cred); + return (-1); + } + + prcp->pr_euid = cr.cr_uid; + prcp->pr_ruid = cr.cr_ruid; + prcp->pr_suid = cr.cr_suid; + prcp->pr_egid = cr.cr_gid; + prcp->pr_rgid = cr.cr_rgid; + prcp->pr_sgid = cr.cr_sgid; + + if (cr.cr_grps == 0) { + prcp->pr_ngroups = 0; + return (0); + } + + if (mdb_vread(&crgrp, sizeof (crgrp), (uintptr_t)cr.cr_grps) != + sizeof (crgrp)) { + mdb_warn("Failed to read credgrp_t from %p\n", cr.cr_grps); + return (-1); + } + + prcp->pr_ngroups = MIN(ngroups, crgrp.crg_ngroups); + for (i = 0; i < prcp->pr_ngroups; i++) { + prcp->pr_groups[i] = crgrp.crg_groups[i]; + } + + return (0); +} + +/*ARGSUSED*/ +static int +Ppriv_gcore(struct ps_prochandle *P, prpriv_t **pprv, void *data) +{ + mdb_proc_t *p = data; + prpriv_t *pp; + cred_t cr; + priv_set_t *psa; + size_t pprv_size; + int i; + + pprv_size = sizeof (prpriv_t) + PRIV_SETBYTES - sizeof (priv_chunk_t) + + prinfo.priv_infosize; + + pp = malloc(pprv_size); + if (pp == NULL) { + return (-1); + } + + if (mdb_vread(&cr, sizeof (cr), p->p_cred) != sizeof (cr)) { + mdb_warn("Failed to read cred_t from %p\n", p->p_cred); + free(pp); + return (-1); + } + + pp->pr_nsets = PRIV_NSET; + pp->pr_setsize = PRIV_SETSIZE; + pp->pr_infosize = prinfo.priv_infosize; + + psa = (priv_set_t *)pp->pr_sets; + for (i = 0; i < PRIV_NSET; i++) { + psa[i] = *gcore_priv_getset(&cr, i); + } + + gcore_priv_getinfo(&cr, (char *)pp + PRIV_PRPRIV_INFO_OFFSET(pp)); + + *pprv = pp; + return (0); +} + +/* + * Fields not filled populated: + * - pr_utime + * - pr_stkbase + * - pr_cutime + * - pr_cstime + * - pr_agentid + */ +/*ARGSUSED*/ +static void +Pstatus_gcore(struct ps_prochandle *P, pstatus_t *sp, void *data) +{ + mdb_proc_t *p = data; + uintptr_t t_addr; + mdb_kthread_t kthr; + mdb_kthread_t *t; + pcommon_t pc; + + t_addr = gcore_prchoose(p); + if (t_addr != NULL) { + if (mdb_ctf_vread(&kthr, "kthread_t", "mdb_kthread_t", t_addr, + 0) == -1) { + return; + } + t = &kthr; + } + + /* just bzero the process part, prgetlwpstatus() does the rest */ + bzero(sp, sizeof (pstatus_t) - sizeof (lwpstatus_t)); + + if (pcommon_init(p, &pc) == -1) { + return; + } + sp->pr_nlwp = pc.pc_nlwp; + sp->pr_nzomb = pc.pc_nzomb; + sp->pr_pid = pc.pc_pid; + sp->pr_ppid = pc.pc_ppid; + sp->pr_pgid = pc.pc_pgid; + sp->pr_sid = pc.pc_sid; + sp->pr_taskid = pc.pc_taskid; + sp->pr_projid = pc.pc_projid; + sp->pr_zoneid = pc.pc_zoneid; + sp->pr_dmodel = pc.pc_dmodel; + + prassignset(&sp->pr_sigpend, &p->p_sig); + sp->pr_brkbase = p->p_brkbase; + sp->pr_brksize = p->p_brksize; + sp->pr_stkbase = gcore_prgetstackbase(p); + sp->pr_stksize = p->p_stksize; + + prassignset(&sp->pr_sigtrace, &p->p_sigmask); + prassignset(&sp->pr_flttrace, &p->p_fltmask); + prassignset(&sp->pr_sysentry, &PTOU(p)->u_entrymask); + prassignset(&sp->pr_sysexit, &PTOU(p)->u_exitmask); + + /* get the chosen lwp's status */ + gcore_prgetlwpstatus(p, t_addr, t, &sp->pr_lwp, NULL); + + /* replicate the flags */ + sp->pr_flags = sp->pr_lwp.pr_flags; +} + +/* + * Fields not populated: + * - pr_contract + * - pr_addr + * - pr_rtime + * - pr_ctime + * - pr_ttydev + * - pr_pctcpu + * - pr_size + * - pr_rsize + * - pr_pctmem + */ +/*ARGSUSED*/ +static const psinfo_t * +Ppsinfo_gcore(struct ps_prochandle *P, psinfo_t *psp, void *data) +{ + mdb_proc_t *p = data; + mdb_kthread_t *t; + mdb_pool_t pool; + cred_t cr; + uintptr_t t_addr; + pcommon_t pc; + + if ((t_addr = gcore_prchoose(p)) == NULL) { + bzero(psp, sizeof (*psp)); + } else { + bzero(psp, sizeof (*psp) - sizeof (psp->pr_lwp)); + } + + if (pcommon_init(p, &pc) == -1) { + return (NULL); + } + psp->pr_nlwp = pc.pc_nlwp; + psp->pr_nzomb = pc.pc_nzomb; + psp->pr_pid = pc.pc_pid; + psp->pr_ppid = pc.pc_ppid; + psp->pr_pgid = pc.pc_pgid; + psp->pr_sid = pc.pc_sid; + psp->pr_taskid = pc.pc_taskid; + psp->pr_projid = pc.pc_projid; + psp->pr_dmodel = pc.pc_dmodel; + + /* + * only export SSYS and SMSACCT; everything else is off-limits to + * userland apps. + */ + psp->pr_flag = p->p_flag & (SSYS | SMSACCT); + + if (mdb_vread(&cr, sizeof (cr), p->p_cred) != sizeof (cr)) { + mdb_warn("Failed to read cred_t from %p\n", p->p_cred); + return (NULL); + } + + psp->pr_uid = cr.cr_ruid; + psp->pr_euid = cr.cr_uid; + psp->pr_gid = cr.cr_rgid; + psp->pr_egid = cr.cr_gid; + + if (mdb_ctf_vread(&pool, "pool_t", "mdb_pool_t", p->p_pool, 0) == -1) { + return (NULL); + } + psp->pr_poolid = pool.pool_id; + + if (t_addr == 0) { + int wcode = p->p_wcode; + + if (wcode) + psp->pr_wstat = gcore_wstat(wcode, p->p_wdata); + psp->pr_ttydev = PRNODEV; + psp->pr_lwp.pr_state = SZOMB; + psp->pr_lwp.pr_sname = 'Z'; + psp->pr_lwp.pr_bindpro = PBIND_NONE; + psp->pr_lwp.pr_bindpset = PS_NONE; + } else { + mdb_kthread_t kthr; + user_t *up = PTOU(p); + + psp->pr_start = up->u_start; + bcopy(up->u_comm, psp->pr_fname, + MIN(sizeof (up->u_comm), sizeof (psp->pr_fname)-1)); + bcopy(up->u_psargs, psp->pr_psargs, + MIN(PRARGSZ-1, PSARGSZ)); + + psp->pr_argc = up->u_argc; + psp->pr_argv = up->u_argv; + psp->pr_envp = up->u_envp; + + /* get the chosen lwp's lwpsinfo */ + if (mdb_ctf_vread(&kthr, "kthread_t", "mdb_kthread_t", t_addr, + 0) == -1) { + return (NULL); + } + t = &kthr; + + gcore_prgetlwpsinfo(t_addr, t, &psp->pr_lwp); + } + + return (NULL); +} + +/*ARGSUSED*/ +static prheader_t * +Plstatus_gcore(struct ps_prochandle *P, void *data) +{ + mdb_proc_t *p = data; + int nlwp = p->p_lwpcnt; + size_t ent_size = LSPAN(lwpstatus_t); + + return (gcore_walk_lwps(p, gcore_lstatus_cb, nlwp, ent_size)); +} + +/*ARGSUSED*/ +static prheader_t * +Plpsinfo_gcore(struct ps_prochandle *P, void *data) +{ + mdb_proc_t *p = data; + int nlwp = p->p_lwpcnt + p->p_zombcnt; + size_t ent_size = LSPAN(lwpsinfo_t); + + return (gcore_walk_lwps(p, gcore_lpsinfo_cb, nlwp, ent_size)); +} + +/*ARGSUSED*/ +static char * +Pplatform_gcore(struct ps_prochandle *P, char *s, size_t n, void *data) +{ + char platform[SYS_NMLN]; + + if (mdb_readvar(platform, "platform") == -1) { + mdb_warn("failed to read platform!\n"); + return (NULL); + } + dprintf("platform: %s\n", platform); + + (void) strncpy(s, platform, n); + return (s); +} + +/*ARGSUSED*/ +static int +Puname_gcore(struct ps_prochandle *P, struct utsname *u, void *data) +{ + if (mdb_readvar(u, "utsname") != sizeof (*u)) { + return (-1); + } + + return (0); +} + +/*ARGSUSED*/ +static char * +Pzonename_gcore(struct ps_prochandle *P, char *s, size_t n, void *data) +{ + mdb_proc_t *p = data; + mdb_zone_t zone; + + if (mdb_ctf_vread(&zone, "zone_t", "mdb_zone_t", p->p_zone, 0) == -1) { + return (NULL); + } + + if (mdb_readstr(s, n, zone.zone_name) == -1) { + mdb_warn("Failed to read zone name from %p\n", zone.zone_name); + return (NULL); + } + + return (s); +} + +/*ARGSUSED*/ +static char * +Pexecname_gcore(struct ps_prochandle *P, char *buf, size_t buflen, void *data) +{ + mdb_proc_t *p = data; + mdb_vnode_t vn; + + if (mdb_ctf_vread(&vn, "vnode_t", "mdb_vnode_t", p->p_exec, 0) == -1) { + return (NULL); + } + + if (mdb_readstr(buf, buflen, vn.v_path) == -1) { + mdb_warn("Failed to read vnode path from %p\n", vn.v_path); + return (NULL); + } + + dprintf("execname: %s\n", buf); + + return (buf); +} + +#if defined(__i386) || defined(__amd64) +/*ARGSUSED*/ +static int +Pldt_gcore(struct ps_prochandle *P, struct ssd *pldt, int nldt, void *data) +{ + mdb_proc_t *p = data; + user_desc_t *udp; + user_desc_t *ldts; + size_t ldt_size; + int i, limit; + + if (p->p_ldt == NULL) { + return (0); + } + + limit = p->p_ldtlimit; + + /* Is this call just to query the size ? */ + if (pldt == NULL || nldt == 0) { + return (limit); + } + + ldt_size = limit * sizeof (*ldts); + ldts = malloc(ldt_size); + if (ldts == NULL) { + mdb_warn("Failed to malloc ldts (size %lld)n", ldt_size); + return (-1); + } + + if (mdb_vread(ldts, ldt_size, p->p_ldt) != ldt_size) { + mdb_warn("Failed to read ldts from %p\n", p->p_ldt); + free(ldts); + return (-1); + } + + for (i = LDT_UDBASE, udp = &ldts[i]; i <= limit; i++, udp++) { + if (udp->usd_type != 0 || udp->usd_dpl != 0 || + udp->usd_p != 0) { + gcore_usd_to_ssd(udp, pldt++, SEL_LDT(i)); + } + } + + free(ldts); + return (limit); +} +#endif + +static const ps_ops_t Pgcore_ops = { + .pop_pread = Pread_gcore, + .pop_pwrite = Pwrite_gcore, + .pop_read_maps = Pread_maps_gcore, + .pop_read_aux = Pread_aux_gcore, + .pop_cred = Pcred_gcore, + .pop_priv = Ppriv_gcore, + .pop_psinfo = Ppsinfo_gcore, + .pop_status = Pstatus_gcore, + .pop_lstatus = Plstatus_gcore, + .pop_lpsinfo = Plpsinfo_gcore, + .pop_platform = Pplatform_gcore, + .pop_uname = Puname_gcore, + .pop_zonename = Pzonename_gcore, + .pop_execname = Pexecname_gcore, +#if defined(__i386) || defined(__amd64) + .pop_ldt = Pldt_gcore +#endif +}; + +/*ARGSUSED*/ +int +gcore_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + struct ps_prochandle *P; + char core_name[MAXNAMELEN]; + mdb_proc_t p; + mdb_pid_t pid; + int error; + + if (!gcore_initialized) { + mdb_warn("gcore unavailable\n"); + return (DCMD_ERR); + } + + if (mdb_ctf_vread(&p, "proc_t", "mdb_proc_t", addr, 0) == -1) { + return (DCMD_ERR); + } + + if (p.p_flag & SSYS) { + mdb_warn("'%s' is a system process\n", p.p_user.u_comm); + return (DCMD_ERR); + } + + if (mdb_ctf_vread(&pid, "struct pid", "mdb_pid_t", p.p_pidp, 0) + == -1) { + return (DCMD_ERR); + } + + if ((P = Pgrab_ops(pid.pid_id, &p, &Pgcore_ops, PGRAB_INCORE)) == + NULL) { + mdb_warn("Failed to initialize proc handle"); + return (DCMD_ERR); + } + + (void) snprintf(core_name, sizeof (core_name), "core.%s.%d", + p.p_user.u_comm, pid.pid_id); + + if ((error = Pgcore(P, core_name, CC_CONTENT_DEFAULT)) != 0) { + mdb_warn("Failed to generate core file: %d", error); + Pfree(P); + return (DCMD_ERR); + } + + Pfree(P); + mdb_printf("Created core file: %s\n", core_name); + + return (0); +} + +void +gcore_init(void) +{ + GElf_Sym sym; + uintptr_t priv_info_addr; + + if (mdb_lookup_by_name("segvn_ops", &sym) == -1) { + mdb_warn("Failed to lookup symbol 'segvn_ops'\n"); + return; + } + gcore_segvn_ops = sym.st_value; + + if (mdb_readvar(&priv_info_addr, "priv_info") == -1) { + mdb_warn("Failed to read variable 'priv_info'\n"); + return; + } + + if (mdb_vread(&prinfo, sizeof (prinfo), priv_info_addr) == -1) { + mdb_warn("Failed to read prinfo from %p\n", priv_info_addr); + return; + } + + if (mdb_lookup_by_name("sclass", &sym) == -1) { + mdb_warn("Failed to lookup symbol 'segvn_ops'\n"); + return; + } + + gcore_sclass = mdb_zalloc(sym.st_size, UM_SLEEP); + if (mdb_vread(gcore_sclass, sym.st_size, sym.st_value) != sym.st_size) { + mdb_warn("Failed to read sclass' from %p\n", sym.st_value); + return; + } + + if (mdb_lookup_by_name("kas", &sym) == -1) { + mdb_warn("Failed to lookup symbol 'kas'\n"); + return; + } + gcore_kas = sym.st_value; + + gcore_initialized = B_TRUE; +} + +#endif /* _KMDB */ diff --git a/usr/src/cmd/mdb/common/modules/genunix/gcore.h b/usr/src/cmd/mdb/common/modules/genunix/gcore.h new file mode 100644 index 0000000000..17c75db189 --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/genunix/gcore.h @@ -0,0 +1,29 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + */ + +#ifndef _MDB_GCORE_H +#define _MDB_GCORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern void gcore_init(void); +extern int gcore_dcmd(uintptr_t, uint_t, int, const mdb_arg_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _MDB_GCORE_H */ diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c index 3f9ee389a2..786b898630 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c +++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c @@ -22,6 +22,7 @@ * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 Joyent, Inc. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. */ #include <mdb/mdb_param.h> @@ -75,6 +76,7 @@ #include "devinfo.h" #include "findstack.h" #include "fm.h" +#include "gcore.h" #include "group.h" #include "irm.h" #include "kgrep.h" @@ -4164,6 +4166,11 @@ static const mdb_dcmd_t dcmds[] = { { "zsd", ":[-v] [zsd_key]", "display zone-specific-data entries for " "selected zones", zsd }, +#ifndef _KMDB + { "gcore", NULL, "generate a user core for the given process", + gcore_dcmd }, +#endif + { NULL } }; @@ -4581,6 +4588,10 @@ _mdb_init(void) (void) mdb_callback_add(MDB_CALLBACK_STCHG, genunix_statechange_cb, NULL); +#ifndef _KMDB + gcore_init(); +#endif + return (&modinfo); } diff --git a/usr/src/cmd/mdb/intel/amd64/genunix/Makefile b/usr/src/cmd/mdb/intel/amd64/genunix/Makefile index fb372b267b..8a284bb2d0 100644 --- a/usr/src/cmd/mdb/intel/amd64/genunix/Makefile +++ b/usr/src/cmd/mdb/intel/amd64/genunix/Makefile @@ -24,6 +24,8 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2013 by Delphix. All rights reserved. +# MODULE = genunix.so MDBTGT = kvm @@ -50,7 +52,7 @@ include ../../../../Makefile.cmd.64 include ../../Makefile.amd64 include ../../../Makefile.module -dmod/$(MODULE) := LDLIBS += -lm +dmod/$(MODULE) := LDLIBS += -lm -lproc # # We are not actually hardwiring some dependency on i86pc, we just need to @@ -72,3 +74,5 @@ CERRWARN += -_gcc=-Wno-unused-label CERRWARN += -_gcc=-Wno-uninitialized CERRWARN += -_gcc=-Wno-parentheses CERRWARN += -_gcc=-Wno-type-limits + +LINTFLAGS64 += -erroff=E_EMPTY_TRANSLATION_UNIT diff --git a/usr/src/cmd/mdb/intel/ia32/genunix/Makefile b/usr/src/cmd/mdb/intel/ia32/genunix/Makefile index e5efcd19ac..354dc8e6b2 100644 --- a/usr/src/cmd/mdb/intel/ia32/genunix/Makefile +++ b/usr/src/cmd/mdb/intel/ia32/genunix/Makefile @@ -22,6 +22,8 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2013 by Delphix. All rights reserved. +# MODULE = genunix.so MDBTGT = kvm @@ -47,7 +49,7 @@ include ../../../../Makefile.cmd include ../../Makefile.ia32 include ../../../Makefile.module -dmod/$(MODULE) := LDLIBS += -lm +dmod/$(MODULE) := LDLIBS += -lm -lproc # # We are not actually hardwiring some dependency on i86pc, we just need to @@ -69,3 +71,5 @@ CERRWARN += -_gcc=-Wno-unused-label CERRWARN += -_gcc=-Wno-uninitialized CERRWARN += -_gcc=-Wno-parentheses CERRWARN += -_gcc=-Wno-type-limits + +LINTFLAGS += -erroff=E_EMPTY_TRANSLATION_UNIT diff --git a/usr/src/cmd/mdb/intel/ia32/libpython2.6/Makefile b/usr/src/cmd/mdb/intel/ia32/libpython2.6/Makefile index ab57dbd02a..1928c56cd3 100644 --- a/usr/src/cmd/mdb/intel/ia32/libpython2.6/Makefile +++ b/usr/src/cmd/mdb/intel/ia32/libpython2.6/Makefile @@ -34,7 +34,7 @@ include ../../../../Makefile.cmd include ../../Makefile.ia32 include ../../../Makefile.module -$(__GNUC)CPPFLAGS += -isystem $(ADJUNCT_PROTO)/usr/include +%.o := CPPFLAGS += -_gcc=-isystem -_gcc=$(ADJUNCT_PROTO)/usr/include dmod/$(MODULE) := LDLIBS += -lproc |