diff options
author | Jeremy Jones <jeremy@delphix.com> | 2013-08-21 15:45:46 -0800 |
---|---|---|
committer | Christopher Siden <chris.siden@delphix.com> | 2013-08-21 16:45:46 -0700 |
commit | 2a12f85ad140e332791b4bad1208a734c3f26bf3 (patch) | |
tree | f4360a28249f0409ce5037f493a18f60e6219ea2 | |
parent | 840b2722e5294ae44aa3af6189002f521d7c50e0 (diff) | |
download | illumos-joyent-2a12f85ad140e332791b4bad1208a734c3f26bf3.tar.gz |
3946 ::gcore
Reviewed by: Adam Leventhal <ahl@delphix.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Robert Mustacchi <rm@joyent.com>
31 files changed, 3386 insertions, 420 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 7bf662a300..9d08a18c69 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) 2012 Joyent, Inc. All rights reserved. */ @@ -207,6 +207,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 8fe234dd53..5c36997799 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" @@ -4188,6 +4190,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 } }; @@ -4605,6 +4612,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/ptools/ppriv/ppriv.c b/usr/src/cmd/ptools/ppriv/ppriv.c index a695c1a09b..b696c63eb7 100644 --- a/usr/src/cmd/ptools/ppriv/ppriv.c +++ b/usr/src/cmd/ptools/ppriv/ppriv.c @@ -20,7 +20,11 @@ */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * + */ +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + */ +/* * Program to examine or set process privileges. */ @@ -158,9 +162,6 @@ main(int argc, char **argv) static int look(char *arg) { - static size_t pprivsz = sizeof (prpriv_t); - static prpriv_t *ppriv; - struct ps_prochandle *Pr; int gcode; size_t sz; @@ -168,6 +169,7 @@ look(char *arg) char *x; int i; boolean_t nodata; + prpriv_t *ppriv; procname = arg; /* for perr() */ @@ -179,15 +181,11 @@ look(char *arg) return (1); } - if (ppriv == NULL) - ppriv = malloc(pprivsz); - - if (Ppriv(Pr, ppriv, pprivsz) == -1) { + if (Ppriv(Pr, &ppriv) == -1) { perr(command); Prelease(Pr, 0); return (1); } - sz = PRIV_PRPRIV_SIZE(ppriv); /* @@ -202,28 +200,20 @@ look(char *arg) "%s: %s: bad PRNOTES section, size = %lx\n", command, arg, (long)sz); Prelease(Pr, 0); + free(ppriv); return (1); } - if (sz > pprivsz) { - ppriv = realloc(ppriv, sz); - - if (ppriv == NULL || Ppriv(Pr, ppriv, sz) != sz) { - perr(command); - Prelease(Pr, 0); - return (1); - } - pprivsz = sz; - } - if (set) { privupdate(ppriv, arg); if (Psetpriv(Pr, ppriv) != 0) { perr(command); Prelease(Pr, 0); + free(ppriv); return (1); } Prelease(Pr, 0); + free(ppriv); return (0); } @@ -298,6 +288,7 @@ look(char *arg) } } Prelease(Pr, 0); + free(ppriv); return (0); } diff --git a/usr/src/lib/libproc/Makefile.com b/usr/src/lib/libproc/Makefile.com index cca3c3255f..30790b7a6f 100644 --- a/usr/src/lib/libproc/Makefile.com +++ b/usr/src/lib/libproc/Makefile.com @@ -21,6 +21,7 @@ # # Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2012 DEY Storage Systems, Inc. All rights reserved. +# Copyright (c) 2013 by Delphix. All rights reserved. # LIBRARY = libproc.a @@ -84,6 +85,7 @@ SRCS = $(CMNOBJS:%.o=../common/%.c) $(ISAOBJS:%.o=%.c) LIBS = $(DYNLIB) $(LINTLIB) LDLIBS += -lrtld_db -lelf -lctf -lc +C99MODE = $(C99_ENABLE) CPPFLAGS += $($(MACH64)_CPPFLAGS) SRCDIR = ../common diff --git a/usr/src/lib/libproc/common/Pcontrol.c b/usr/src/lib/libproc/common/Pcontrol.c index 1e7ce1556e..751c0c3f8a 100644 --- a/usr/src/lib/libproc/common/Pcontrol.c +++ b/usr/src/lib/libproc/common/Pcontrol.c @@ -25,6 +25,7 @@ * * Portions Copyright 2007 Chad Mynhier * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. */ #include <assert.h> @@ -41,6 +42,7 @@ #include <limits.h> #include <signal.h> #include <atomic.h> +#include <zone.h> #include <sys/types.h> #include <sys/uio.h> #include <sys/stat.h> @@ -50,6 +52,7 @@ #include <sys/fault.h> #include <sys/syscall.h> #include <sys/sysmacros.h> +#include <sys/systeminfo.h> #include "libproc.h" #include "Pcontrol.h" @@ -71,25 +74,261 @@ char procfs_path[PATH_MAX] = "/proc"; static void deadcheck(struct ps_prochandle *); static void restore_tracing_flags(struct ps_prochandle *); static void Lfree_internal(struct ps_prochandle *, struct ps_lwphandle *); +static prheader_t *read_lfile(struct ps_prochandle *, const char *); /* - * Read/write interface for live processes: just pread/pwrite the - * /proc/<pid>/as file: + * Ops vector functions for live processes. */ +/*ARGSUSED*/ static ssize_t -Pread_live(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr) +Pread_live(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr, + void *data) { return (pread(P->asfd, buf, n, (off_t)addr)); } +/*ARGSUSED*/ static ssize_t -Pwrite_live(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr) +Pwrite_live(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr, + void *data) { return (pwrite(P->asfd, buf, n, (off_t)addr)); } -static const ps_rwops_t P_live_ops = { Pread_live, Pwrite_live }; +/*ARGSUSED*/ +static int +Pread_maps_live(struct ps_prochandle *P, prmap_t **Pmapp, ssize_t *nmapp, + void *data) +{ + char mapfile[PATH_MAX]; + int mapfd; + struct stat statb; + ssize_t nmap; + prmap_t *Pmap = NULL; + + (void) snprintf(mapfile, sizeof (mapfile), "%s/%d/map", + procfs_path, (int)P->pid); + if ((mapfd = open(mapfile, O_RDONLY)) < 0 || + fstat(mapfd, &statb) != 0 || + statb.st_size < sizeof (prmap_t) || + (Pmap = malloc(statb.st_size)) == NULL || + (nmap = pread(mapfd, Pmap, statb.st_size, 0L)) <= 0 || + (nmap /= sizeof (prmap_t)) == 0) { + if (Pmap != NULL) + free(Pmap); + if (mapfd >= 0) + (void) close(mapfd); + Preset_maps(P); /* utter failure; destroy tables */ + return (-1); + } + (void) close(mapfd); + + *Pmapp = Pmap; + *nmapp = nmap; + + return (0); +} + +/*ARGSUSED*/ +static void +Pread_aux_live(struct ps_prochandle *P, auxv_t **auxvp, int *nauxp, void *data) +{ + char auxfile[64]; + int fd; + struct stat statb; + auxv_t *auxv; + ssize_t naux; + + (void) snprintf(auxfile, sizeof (auxfile), "%s/%d/auxv", + procfs_path, (int)P->pid); + if ((fd = open(auxfile, O_RDONLY)) < 0) { + dprintf("%s: failed to open %s: %s\n", + __func__, auxfile, strerror(errno)); + return; + } + + if (fstat(fd, &statb) == 0 && + statb.st_size >= sizeof (auxv_t) && + (auxv = malloc(statb.st_size + sizeof (auxv_t))) != NULL) { + if ((naux = read(fd, auxv, statb.st_size)) < 0 || + (naux /= sizeof (auxv_t)) < 1) { + dprintf("%s: read failed: %s\n", + __func__, strerror(errno)); + free(auxv); + } else { + auxv[naux].a_type = AT_NULL; + auxv[naux].a_un.a_val = 0L; + + *auxvp = auxv; + *nauxp = (int)naux; + } + } + + (void) close(fd); +} + +/*ARGSUSED*/ +static int +Pcred_live(struct ps_prochandle *P, prcred_t *pcrp, int ngroups, void *data) +{ + return (proc_get_cred(P->pid, pcrp, ngroups)); +} + +/*ARGSUSED*/ +static int +Ppriv_live(struct ps_prochandle *P, prpriv_t **pprv, void *data) +{ + prpriv_t *pp; + + pp = proc_get_priv(P->pid); + if (pp == NULL) { + return (-1); + } + + *pprv = pp; + return (0); +} + +/*ARGSUSED*/ +static const psinfo_t * +Ppsinfo_live(struct ps_prochandle *P, psinfo_t *psinfo, void *data) +{ + if (proc_get_psinfo(P->pid, psinfo) == -1) + return (NULL); + + return (psinfo); +} + +/*ARGSUSED*/ +static prheader_t * +Plstatus_live(struct ps_prochandle *P, void *data) +{ + return (read_lfile(P, "lstatus")); +} + +/*ARGSUSED*/ +static prheader_t * +Plpsinfo_live(struct ps_prochandle *P, void *data) +{ + return (read_lfile(P, "lpsinfo")); +} + +/*ARGSUSED*/ +static char * +Pplatform_live(struct ps_prochandle *P, char *s, size_t n, void *data) +{ + if (sysinfo(SI_PLATFORM, s, n) == -1) + return (NULL); + return (s); +} + +/*ARGSUSED*/ +static int +Puname_live(struct ps_prochandle *P, struct utsname *u, void *data) +{ + return (uname(u)); +} + +/*ARGSUSED*/ +static char * +Pzonename_live(struct ps_prochandle *P, char *s, size_t n, void *data) +{ + if (getzonenamebyid(P->status.pr_zoneid, s, n) < 0) + return (NULL); + s[n - 1] = '\0'; + return (s); +} + +/* + * Callback function for Pfindexec(). We return a match if we can stat the + * suggested pathname and confirm its device and inode number match our + * previous information about the /proc/<pid>/object/a.out file. + */ +static int +stat_exec(const char *path, void *arg) +{ + struct stat64 *stp = arg; + struct stat64 st; + + return (stat64(path, &st) == 0 && S_ISREG(st.st_mode) && + stp->st_dev == st.st_dev && stp->st_ino == st.st_ino); +} + +/*ARGSUSED*/ +static char * +Pexecname_live(struct ps_prochandle *P, char *buf, size_t buflen, void *data) +{ + char exec_name[PATH_MAX]; + char cwd[PATH_MAX]; + char proc_cwd[64]; + struct stat64 st; + int ret; + + /* + * Try to get the path information first. + */ + (void) snprintf(exec_name, sizeof (exec_name), + "%s/%d/path/a.out", procfs_path, (int)P->pid); + if ((ret = readlink(exec_name, buf, buflen - 1)) > 0) { + buf[ret] = '\0'; + (void) Pfindobj(P, buf, buf, buflen); + return (buf); + } + + /* + * Stat the executable file so we can compare Pfindexec's + * suggestions to the actual device and inode number. + */ + (void) snprintf(exec_name, sizeof (exec_name), + "%s/%d/object/a.out", procfs_path, (int)P->pid); + + if (stat64(exec_name, &st) != 0 || !S_ISREG(st.st_mode)) + return (NULL); + + /* + * Attempt to figure out the current working directory of the + * target process. This only works if the target process has + * not changed its current directory since it was exec'd. + */ + (void) snprintf(proc_cwd, sizeof (proc_cwd), + "%s/%d/path/cwd", procfs_path, (int)P->pid); + + if ((ret = readlink(proc_cwd, cwd, PATH_MAX - 1)) > 0) + cwd[ret] = '\0'; + + (void) Pfindexec(P, ret > 0 ? cwd : NULL, stat_exec, &st); + + return (NULL); +} + +#if defined(__i386) || defined(__amd64) +/*ARGSUSED*/ +static int +Pldt_live(struct ps_prochandle *P, struct ssd *pldt, int nldt, void *data) +{ + return (proc_get_ldt(P->pid, pldt, nldt)); +} +#endif + +static const ps_ops_t P_live_ops = { + .pop_pread = Pread_live, + .pop_pwrite = Pwrite_live, + .pop_read_maps = Pread_maps_live, + .pop_read_aux = Pread_aux_live, + .pop_cred = Pcred_live, + .pop_priv = Ppriv_live, + .pop_psinfo = Ppsinfo_live, + .pop_lstatus = Plstatus_live, + .pop_lpsinfo = Plpsinfo_live, + .pop_platform = Pplatform_live, + .pop_uname = Puname_live, + .pop_zonename = Pzonename_live, + .pop_execname = Pexecname_live, +#if defined(__i386) || defined(__amd64) + .pop_ldt = Pldt_live +#endif +}; /* * This is the library's .init handler. @@ -249,7 +488,7 @@ Pxcreate(const char *file, /* executable file name */ P->statfd = -1; P->agentctlfd = -1; P->agentstatfd = -1; - P->ops = &P_live_ops; + Pinit_ops(&P->ops, &P_live_ops); Pinitsym(P); /* @@ -552,7 +791,7 @@ again: /* Come back here if we lose it in the Window of Vulnerability */ P->statfd = -1; P->agentctlfd = -1; P->agentstatfd = -1; - P->ops = &P_live_ops; + Pinit_ops(&P->ops, &P_live_ops); Pinitsym(P); /* @@ -933,45 +1172,6 @@ Pfree(struct ps_prochandle *P) { uint_t i; - if (P->core != NULL) { - extern void __priv_free_info(void *); - lwp_info_t *nlwp, *lwp = list_next(&P->core->core_lwp_head); - - for (i = 0; i < P->core->core_nlwp; i++, lwp = nlwp) { - nlwp = list_next(lwp); -#ifdef __sparc - if (lwp->lwp_gwins != NULL) - free(lwp->lwp_gwins); - if (lwp->lwp_xregs != NULL) - free(lwp->lwp_xregs); - if (lwp->lwp_asrs != NULL) - free(lwp->lwp_asrs); -#endif - free(lwp); - } - - if (P->core->core_platform != NULL) - free(P->core->core_platform); - if (P->core->core_uts != NULL) - free(P->core->core_uts); - if (P->core->core_cred != NULL) - free(P->core->core_cred); - if (P->core->core_priv != NULL) - free(P->core->core_priv); - if (P->core->core_privinfo != NULL) - __priv_free_info(P->core->core_privinfo); - if (P->core->core_ppii != NULL) - free(P->core->core_ppii); - if (P->core->core_zonename != NULL) - free(P->core->core_zonename); -#if defined(__i386) || defined(__amd64) - if (P->core->core_ldt != NULL) - free(P->core->core_ldt); -#endif - - free(P->core); - } - if (P->ucaddrs != NULL) { free(P->ucaddrs); P->ucaddrs = NULL; @@ -1008,6 +1208,7 @@ Pfree(struct ps_prochandle *P) if (P->statfd >= 0) (void) close(P->statfd); Preset_maps(P); + P->ops.pop_fini(P, P->data); /* clear out the structure as a precaution against reuse */ (void) memset(P, 0, sizeof (*P)); @@ -1059,15 +1260,7 @@ Pctlfd(struct ps_prochandle *P) const psinfo_t * Ppsinfo(struct ps_prochandle *P) { - if (P->state == PS_IDLE) { - errno = ENODATA; - return (NULL); - } - - if (P->state != PS_DEAD && proc_get_psinfo(P->pid, &P->psinfo) == -1) - return (NULL); - - return (&P->psinfo); + return (P->ops.pop_psinfo(P, &P->psinfo, P->data)); } /* @@ -1081,6 +1274,12 @@ Pstatus(struct ps_prochandle *P) return (&P->status); } +static void +Pread_status(struct ps_prochandle *P) +{ + P->ops.pop_status(P, &P->status, P->data); +} + /* * Fill in a pointer to a process credentials structure. The ngroups parameter * is the number of supplementary group entries allocated in the caller's cred @@ -1090,32 +1289,22 @@ Pstatus(struct ps_prochandle *P) int Pcred(struct ps_prochandle *P, prcred_t *pcrp, int ngroups) { - if (P->state == PS_IDLE) { - errno = ENODATA; - return (-1); - } - - if (P->state != PS_DEAD) - return (proc_get_cred(P->pid, pcrp, ngroups)); - - if (P->core->core_cred != NULL) { - /* - * Avoid returning more supplementary group data than the - * caller has allocated in their buffer. We expect them to - * check pr_ngroups afterward and potentially call us again. - */ - ngroups = MIN(ngroups, P->core->core_cred->pr_ngroups); - - (void) memcpy(pcrp, P->core->core_cred, - sizeof (prcred_t) + (ngroups - 1) * sizeof (gid_t)); + return (P->ops.pop_cred(P, pcrp, ngroups, P->data)); +} - return (0); - } +static prheader_t * +Plstatus(struct ps_prochandle *P) +{ + return (P->ops.pop_lstatus(P, P->data)); +} - errno = ENODATA; - return (-1); +static prheader_t * +Plpsinfo(struct ps_prochandle *P) +{ + return (P->ops.pop_lpsinfo(P, P->data)); } + #if defined(__i386) || defined(__amd64) /* * Fill in a pointer to a process LDT structure. @@ -1126,55 +1315,18 @@ Pcred(struct ps_prochandle *P, prcred_t *pcrp, int ngroups) int Pldt(struct ps_prochandle *P, struct ssd *pldt, int nldt) { - if (P->state == PS_IDLE) { - errno = ENODATA; - return (-1); - } - - if (P->state != PS_DEAD) - return (proc_get_ldt(P->pid, pldt, nldt)); - - if (pldt == NULL || nldt == 0) - return (P->core->core_nldt); - - if (P->core->core_ldt != NULL) { - nldt = MIN(nldt, P->core->core_nldt); + return (P->ops.pop_ldt(P, pldt, nldt, P->data)); - (void) memcpy(pldt, P->core->core_ldt, - nldt * sizeof (struct ssd)); - - return (nldt); - } - - errno = ENODATA; - return (-1); } #endif /* __i386 */ /* - * Fill in a pointer to a process privilege structure. + * Return a malloced process privilege structure in *pprv. */ -ssize_t -Ppriv(struct ps_prochandle *P, prpriv_t *pprv, size_t size) -{ - if (P->state != PS_DEAD) { - prpriv_t *pp = proc_get_priv(P->pid); - if (pp != NULL) { - size = MIN(size, PRIV_PRPRIV_SIZE(pp)); - (void) memcpy(pprv, pp, size); - free(pp); - return (size); - } - return (-1); - } - - if (P->core->core_priv != NULL) { - size = MIN(P->core->core_priv_size, size); - (void) memcpy(pprv, P->core->core_priv, size); - return (size); - } - errno = ENODATA; - return (-1); +int +Ppriv(struct ps_prochandle *P, prpriv_t **pprv) +{ + return (P->ops.pop_priv(P, pprv, P->data)); } int @@ -1214,11 +1366,13 @@ Psetpriv(struct ps_prochandle *P, prpriv_t *pprv) void * Pprivinfo(struct ps_prochandle *P) { + core_info_t *core = P->data; + /* Use default from libc */ if (P->state != PS_DEAD) return (NULL); - return (P->core->core_privinfo); + return (core->core_privinfo); } /* @@ -1978,12 +2132,12 @@ Pread(struct ps_prochandle *P, size_t nbyte, /* number of bytes to read */ uintptr_t address) /* address in process */ { - return (P->ops->p_pread(P, buf, nbyte, address)); + return (P->ops.pop_pread(P, buf, nbyte, address, P->data)); } ssize_t Pread_string(struct ps_prochandle *P, - char *buf, /* caller's buffer */ + char *buf, /* caller's buffer */ size_t size, /* upper limit on bytes to read */ uintptr_t addr) /* address in process */ { @@ -2003,7 +2157,8 @@ Pread_string(struct ps_prochandle *P, string[STRSZ] = '\0'; for (nbyte = STRSZ; nbyte == STRSZ && leng < size; addr += STRSZ) { - if ((nbyte = P->ops->p_pread(P, string, STRSZ, addr)) <= 0) { + if ((nbyte = P->ops.pop_pread(P, string, STRSZ, addr, + P->data)) <= 0) { buf[leng] = '\0'; return (leng ? leng : -1); } @@ -2024,7 +2179,7 @@ Pwrite(struct ps_prochandle *P, size_t nbyte, /* number of bytes to write */ uintptr_t address) /* address in process */ { - return (P->ops->p_pwrite(P, buf, nbyte, address)); + return (P->ops.pop_pwrite(P, buf, nbyte, address, P->data)); } int @@ -2765,10 +2920,11 @@ Plwp_iter(struct ps_prochandle *P, proc_lwp_f *func, void *cd) * list of LWP structs we read in from the core file. */ if (P->state == PS_DEAD) { - lwp_info_t *lwp = list_prev(&P->core->core_lwp_head); + core_info_t *core = P->data; + lwp_info_t *lwp = list_prev(&core->core_lwp_head); uint_t i; - for (i = 0; i < P->core->core_nlwp; i++, lwp = list_prev(lwp)) { + for (i = 0; i < core->core_nlwp; i++, lwp = list_prev(lwp)) { if (lwp->lwp_psinfo.pr_sname != 'Z' && (rv = func(cd, &lwp->lwp_status)) != 0) break; @@ -2781,7 +2937,7 @@ Plwp_iter(struct ps_prochandle *P, proc_lwp_f *func, void *cd) * For the live process multi-LWP case, we have to work a little * harder: the /proc/pid/lstatus file has the array of LWP structs. */ - if ((Lhp = read_lfile(P, "lstatus")) == NULL) + if ((Lhp = Plstatus(P)) == NULL) return (-1); for (nlwp = Lhp->pr_nent, Lsp = (lwpstatus_t *)(uintptr_t)(Lhp + 1); @@ -2836,10 +2992,11 @@ retry: * list of LWP structs we read in from the core file. */ if (P->state == PS_DEAD) { - lwp_info_t *lwp = list_prev(&P->core->core_lwp_head); + core_info_t *core = P->data; + lwp_info_t *lwp = list_prev(&core->core_lwp_head); uint_t i; - for (i = 0; i < P->core->core_nlwp; i++, lwp = list_prev(lwp)) { + for (i = 0; i < core->core_nlwp; i++, lwp = list_prev(lwp)) { sp = (lwp->lwp_psinfo.pr_sname == 'Z')? NULL : &lwp->lwp_status; if ((rv = func(cd, sp, &lwp->lwp_psinfo)) != 0) @@ -2850,13 +3007,12 @@ retry: } /* - * For the live process multi-LWP case, we have to work a little - * harder: the /proc/pid/lstatus file has the array of lwpstatus_t's - * and the /proc/pid/lpsinfo file has the array of lwpsinfo_t's. + * For all other cases retrieve the array of lwpstatus_t's and + * lwpsinfo_t's. */ - if ((Lhp = read_lfile(P, "lstatus")) == NULL) + if ((Lhp = Plstatus(P)) == NULL) return (-1); - if ((Lphp = read_lfile(P, "lpsinfo")) == NULL) { + if ((Lphp = Plpsinfo(P)) == NULL) { free(Lhp); return (-1); } @@ -2920,8 +3076,10 @@ retry: core_content_t Pcontent(struct ps_prochandle *P) { + core_info_t *core = P->data; + if (P->state == PS_DEAD) - return (P->core->core_content); + return (core->core_content); if (P->state == PS_IDLE) return (CC_CONTENT_TEXT | CC_CONTENT_DATA | CC_CONTENT_CTF); @@ -3728,3 +3886,32 @@ Psort_mappings(struct ps_prochandle *P) mp->map_relocate = 0; } } + +struct ps_prochandle * +Pgrab_ops(pid_t pid, void *data, const ps_ops_t *ops, int flags) +{ + struct ps_prochandle *P; + + if ((P = calloc(1, sizeof (*P))) == NULL) { + return (NULL); + } + + Pinit_ops(&P->ops, ops); + (void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL); + P->pid = pid; + P->state = PS_STOP; + P->asfd = -1; + P->ctlfd = -1; + P->statfd = -1; + P->agentctlfd = -1; + P->agentstatfd = -1; + Pinitsym(P); + P->data = data; + Pread_status(P); + + if (flags & PGRAB_INCORE) { + P->flags |= INCORE; + } + + return (P); +} diff --git a/usr/src/lib/libproc/common/Pcontrol.h b/usr/src/lib/libproc/common/Pcontrol.h index 4bb7e0ca3c..3e72a32804 100644 --- a/usr/src/lib/libproc/common/Pcontrol.h +++ b/usr/src/lib/libproc/common/Pcontrol.h @@ -25,6 +25,7 @@ /* * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. */ #ifndef _PCONTROL_H @@ -43,6 +44,7 @@ #include <libproc.h> #include <libctf.h> #include <limits.h> +#include <libproc.h> #ifdef __cplusplus extern "C" { @@ -190,13 +192,6 @@ typedef struct elf_file { /* convenience for managing ELF files */ int e_fd; /* file descriptor */ } elf_file_t; -typedef struct ps_rwops { /* ops vector for Pread() and Pwrite() */ - ssize_t (*p_pread)(struct ps_prochandle *, - void *, size_t, uintptr_t); - ssize_t (*p_pwrite)(struct ps_prochandle *, - const void *, size_t, uintptr_t); -} ps_rwops_t; - #define HASHSIZE 1024 /* hash table size, power of 2 */ struct ps_prochandle { @@ -227,8 +222,7 @@ struct ps_prochandle { rd_agent_t *rap; /* cookie for rtld_db */ map_info_t *map_exec; /* the mapping for the executable file */ map_info_t *map_ldso; /* the mapping for ld.so.1 */ - const ps_rwops_t *ops; /* pointer to ops-vector for read and write */ - core_info_t *core; /* information specific to core (if PS_DEAD) */ + ps_ops_t ops; /* ops-vector */ uintptr_t *ucaddrs; /* ucontext-list addresses */ uint_t ucnelems; /* number of elements in the ucaddrs list */ char *zoneroot; /* cached path to zone root */ @@ -237,6 +231,7 @@ struct ps_prochandle { uintptr_t map_missing; /* first missing mapping in core due to sig */ siginfo_t killinfo; /* signal that interrupted core dump */ psinfo_t spymaster; /* agent LWP's spymaster, if any */ + void *data; /* private data */ }; /* flags */ @@ -247,6 +242,7 @@ struct ps_prochandle { #define SETEXIT 0x10 /* set sysexit trace mask before continuing */ #define SETHOLD 0x20 /* set signal hold mask before continuing */ #define SETREGS 0x40 /* set registers before continuing */ +#define INCORE 0x80 /* use in-core data to build symbol tables */ struct ps_lwphandle { struct ps_prochandle *lwp_proc; /* process to which this lwp belongs */ diff --git a/usr/src/lib/libproc/common/Pcore.c b/usr/src/lib/libproc/common/Pcore.c index e16e019ab9..596c45861a 100644 --- a/usr/src/lib/libproc/common/Pcore.c +++ b/usr/src/lib/libproc/common/Pcore.c @@ -25,6 +25,7 @@ /* * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. */ #include <sys/types.h> @@ -111,20 +112,202 @@ core_rw(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr, return (n - resid); } +/*ARGSUSED*/ static ssize_t -Pread_core(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr) +Pread_core(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr, + void *data) { return (core_rw(P, buf, n, addr, pread64)); } +/*ARGSUSED*/ static ssize_t -Pwrite_core(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr) +Pwrite_core(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr, + void *data) { return (core_rw(P, (void *)buf, n, addr, (ssize_t (*)(int, void *, size_t, off64_t)) pwrite64)); } -static const ps_rwops_t P_core_ops = { Pread_core, Pwrite_core }; +/*ARGSUSED*/ +static int +Pcred_core(struct ps_prochandle *P, prcred_t *pcrp, int ngroups, void *data) +{ + core_info_t *core = data; + + if (core->core_cred != NULL) { + /* + * Avoid returning more supplementary group data than the + * caller has allocated in their buffer. We expect them to + * check pr_ngroups afterward and potentially call us again. + */ + ngroups = MIN(ngroups, core->core_cred->pr_ngroups); + + (void) memcpy(pcrp, core->core_cred, + sizeof (prcred_t) + (ngroups - 1) * sizeof (gid_t)); + + return (0); + } + + errno = ENODATA; + return (-1); +} + +/*ARGSUSED*/ +static int +Ppriv_core(struct ps_prochandle *P, prpriv_t **pprv, void *data) +{ + core_info_t *core = data; + + if (core->core_priv == NULL) { + errno = ENODATA; + return (-1); + } + + *pprv = malloc(core->core_priv_size); + if (*pprv == NULL) { + return (-1); + } + + (void) memcpy(*pprv, core->core_priv, core->core_priv_size); + return (0); +} + +/*ARGSUSED*/ +static const psinfo_t * +Ppsinfo_core(struct ps_prochandle *P, psinfo_t *psinfo, void *data) +{ + return (&P->psinfo); +} + +/*ARGSUSED*/ +static void +Pfini_core(struct ps_prochandle *P, void *data) +{ + core_info_t *core = data; + + if (core != NULL) { + extern void __priv_free_info(void *); + lwp_info_t *nlwp, *lwp = list_next(&core->core_lwp_head); + int i; + + for (i = 0; i < core->core_nlwp; i++, lwp = nlwp) { + nlwp = list_next(lwp); +#ifdef __sparc + if (lwp->lwp_gwins != NULL) + free(lwp->lwp_gwins); + if (lwp->lwp_xregs != NULL) + free(lwp->lwp_xregs); + if (lwp->lwp_asrs != NULL) + free(lwp->lwp_asrs); +#endif + free(lwp); + } + + if (core->core_platform != NULL) + free(core->core_platform); + if (core->core_uts != NULL) + free(core->core_uts); + if (core->core_cred != NULL) + free(core->core_cred); + if (core->core_priv != NULL) + free(core->core_priv); + if (core->core_privinfo != NULL) + __priv_free_info(core->core_privinfo); + if (core->core_ppii != NULL) + free(core->core_ppii); + if (core->core_zonename != NULL) + free(core->core_zonename); +#if defined(__i386) || defined(__amd64) + if (core->core_ldt != NULL) + free(core->core_ldt); +#endif + + free(core); + } +} + +/*ARGSUSED*/ +static char * +Pplatform_core(struct ps_prochandle *P, char *s, size_t n, void *data) +{ + core_info_t *core = data; + + if (core->core_platform == NULL) { + errno = ENODATA; + return (NULL); + } + (void) strncpy(s, core->core_platform, n - 1); + s[n - 1] = '\0'; + return (s); +} + +/*ARGSUSED*/ +static int +Puname_core(struct ps_prochandle *P, struct utsname *u, void *data) +{ + core_info_t *core = data; + + if (core->core_uts == NULL) { + errno = ENODATA; + return (-1); + } + (void) memcpy(u, core->core_uts, sizeof (struct utsname)); + return (0); +} + +/*ARGSUSED*/ +static char * +Pzonename_core(struct ps_prochandle *P, char *s, size_t n, void *data) +{ + core_info_t *core = data; + + if (core->core_zonename == NULL) { + errno = ENODATA; + return (NULL); + } + (void) strlcpy(s, core->core_zonename, n); + return (s); +} + +#if defined(__i386) || defined(__amd64) +/*ARGSUSED*/ +static int +Pldt_core(struct ps_prochandle *P, struct ssd *pldt, int nldt, void *data) +{ + core_info_t *core = data; + + if (pldt == NULL || nldt == 0) + return (core->core_nldt); + + if (core->core_ldt != NULL) { + nldt = MIN(nldt, core->core_nldt); + + (void) memcpy(pldt, core->core_ldt, + nldt * sizeof (struct ssd)); + + return (nldt); + } + + errno = ENODATA; + return (-1); +} +#endif + +static const ps_ops_t P_core_ops = { + .pop_pread = Pread_core, + .pop_pwrite = Pwrite_core, + .pop_cred = Pcred_core, + .pop_priv = Ppriv_core, + .pop_psinfo = Ppsinfo_core, + .pop_fini = Pfini_core, + .pop_platform = Pplatform_core, + .pop_uname = Puname_core, + .pop_zonename = Pzonename_core, +#if defined(__i386) || defined(__amd64) + .pop_ldt = Pldt_core +#endif +}; /* * Return the lwp_info_t for the given lwpid. If no such lwpid has been @@ -134,13 +317,14 @@ static const ps_rwops_t P_core_ops = { Pread_core, Pwrite_core }; static lwp_info_t * lwpid2info(struct ps_prochandle *P, lwpid_t id) { - lwp_info_t *lwp = list_next(&P->core->core_lwp_head); + core_info_t *core = P->data; + lwp_info_t *lwp = list_next(&core->core_lwp_head); lwp_info_t *next; uint_t i; - for (i = 0; i < P->core->core_nlwp; i++, lwp = list_next(lwp)) { + for (i = 0; i < core->core_nlwp; i++, lwp = list_next(lwp)) { if (lwp->lwp_id == id) { - P->core->core_lwp = lwp; + core->core_lwp = lwp; return (lwp); } if (lwp->lwp_id < id) { @@ -155,8 +339,8 @@ lwpid2info(struct ps_prochandle *P, lwpid_t id) list_link(lwp, next); lwp->lwp_id = id; - P->core->core_lwp = lwp; - P->core->core_nlwp++; + core->core_lwp = lwp; + core->core_nlwp++; return (lwp); } @@ -175,7 +359,9 @@ static int note_pstatus(struct ps_prochandle *P, size_t nbytes) { #ifdef _LP64 - if (P->core->core_dmodel == PR_MODEL_ILP32) { + core_info_t *core = P->data; + + if (core->core_dmodel == PR_MODEL_ILP32) { pstatus32_t ps32; if (nbytes < sizeof (pstatus32_t) || @@ -207,7 +393,9 @@ note_lwpstatus(struct ps_prochandle *P, size_t nbytes) lwpstatus_t lps; #ifdef _LP64 - if (P->core->core_dmodel == PR_MODEL_ILP32) { + core_info_t *core = P->data; + + if (core->core_dmodel == PR_MODEL_ILP32) { lwpstatus32_t l32; if (nbytes < sizeof (lwpstatus32_t) || @@ -246,7 +434,9 @@ static int note_psinfo(struct ps_prochandle *P, size_t nbytes) { #ifdef _LP64 - if (P->core->core_dmodel == PR_MODEL_ILP32) { + core_info_t *core = P->data; + + if (core->core_dmodel == PR_MODEL_ILP32) { psinfo32_t ps32; if (nbytes < sizeof (psinfo32_t) || @@ -278,7 +468,9 @@ note_lwpsinfo(struct ps_prochandle *P, size_t nbytes) lwpsinfo_t lps; #ifdef _LP64 - if (P->core->core_dmodel == PR_MODEL_ILP32) { + core_info_t *core = P->data; + + if (core->core_dmodel == PR_MODEL_ILP32) { lwpsinfo32_t l32; if (nbytes < sizeof (lwpsinfo32_t) || @@ -328,9 +520,10 @@ note_fdinfo(struct ps_prochandle *P, size_t nbytes) static int note_platform(struct ps_prochandle *P, size_t nbytes) { + core_info_t *core = P->data; char *plat; - if (P->core->core_platform != NULL) + if (core->core_platform != NULL) return (0); /* Already seen */ if (nbytes != 0 && ((plat = malloc(nbytes + 1)) != NULL)) { @@ -340,7 +533,7 @@ note_platform(struct ps_prochandle *P, size_t nbytes) return (-1); } plat[nbytes - 1] = '\0'; - P->core->core_platform = plat; + core->core_platform = plat; } return (0); @@ -349,10 +542,11 @@ note_platform(struct ps_prochandle *P, size_t nbytes) static int note_utsname(struct ps_prochandle *P, size_t nbytes) { + core_info_t *core = P->data; size_t ubytes = sizeof (struct utsname); struct utsname *utsp; - if (P->core->core_uts != NULL || nbytes < ubytes) + if (core->core_uts != NULL || nbytes < ubytes) return (0); /* Already seen or bad size */ if ((utsp = malloc(ubytes)) == NULL) @@ -372,22 +566,23 @@ note_utsname(struct ps_prochandle *P, size_t nbytes) dprintf("uts.machine = \"%s\"\n", utsp->machine); } - P->core->core_uts = utsp; + core->core_uts = utsp; return (0); } static int note_content(struct ps_prochandle *P, size_t nbytes) { + core_info_t *core = P->data; core_content_t content; - if (sizeof (P->core->core_content) != nbytes) + if (sizeof (core->core_content) != nbytes) return (-1); if (read(P->asfd, &content, sizeof (content)) != sizeof (content)) return (-1); - P->core->core_content = content; + core->core_content = content; dprintf("core content = %llx\n", content); @@ -397,6 +592,7 @@ note_content(struct ps_prochandle *P, size_t nbytes) static int note_cred(struct ps_prochandle *P, size_t nbytes) { + core_info_t *core = P->data; prcred_t *pcrp; int ngroups; const size_t min_size = sizeof (prcred_t) - sizeof (gid_t); @@ -407,7 +603,7 @@ note_cred(struct ps_prochandle *P, size_t nbytes) * no group memberships. This allows for more flexibility when it * comes to slightly malformed -- but still valid -- notes. */ - if (P->core->core_cred != NULL || nbytes < min_size) + if (core->core_cred != NULL || nbytes < min_size) return (0); /* Already seen or bad size */ ngroups = (nbytes - min_size) / sizeof (gid_t); @@ -428,7 +624,7 @@ note_cred(struct ps_prochandle *P, size_t nbytes) pcrp->pr_ngroups = ngroups; } - P->core->core_cred = pcrp; + core->core_cred = pcrp; return (0); } @@ -436,10 +632,11 @@ note_cred(struct ps_prochandle *P, size_t nbytes) static int note_ldt(struct ps_prochandle *P, size_t nbytes) { + core_info_t *core = P->data; struct ssd *pldt; uint_t nldt; - if (P->core->core_ldt != NULL || nbytes < sizeof (struct ssd)) + if (core->core_ldt != NULL || nbytes < sizeof (struct ssd)) return (0); /* Already seen or bad size */ nldt = nbytes / sizeof (struct ssd); @@ -454,8 +651,8 @@ note_ldt(struct ps_prochandle *P, size_t nbytes) return (-1); } - P->core->core_ldt = pldt; - P->core->core_nldt = nldt; + core->core_ldt = pldt; + core->core_nldt = nldt; return (0); } #endif /* __i386 */ @@ -463,9 +660,10 @@ note_ldt(struct ps_prochandle *P, size_t nbytes) static int note_priv(struct ps_prochandle *P, size_t nbytes) { + core_info_t *core = P->data; prpriv_t *pprvp; - if (P->core->core_priv != NULL || nbytes < sizeof (prpriv_t)) + if (core->core_priv != NULL || nbytes < sizeof (prpriv_t)) return (0); /* Already seen or bad size */ if ((pprvp = malloc(nbytes)) == NULL) @@ -477,18 +675,19 @@ note_priv(struct ps_prochandle *P, size_t nbytes) return (-1); } - P->core->core_priv = pprvp; - P->core->core_priv_size = nbytes; + core->core_priv = pprvp; + core->core_priv_size = nbytes; return (0); } static int note_priv_info(struct ps_prochandle *P, size_t nbytes) { + core_info_t *core = P->data; extern void *__priv_parse_info(); priv_impl_info_t *ppii; - if (P->core->core_privinfo != NULL || + if (core->core_privinfo != NULL || nbytes < sizeof (priv_impl_info_t)) return (0); /* Already seen or bad size */ @@ -502,17 +701,18 @@ note_priv_info(struct ps_prochandle *P, size_t nbytes) return (-1); } - P->core->core_privinfo = __priv_parse_info(ppii); - P->core->core_ppii = ppii; + core->core_privinfo = __priv_parse_info(ppii); + core->core_ppii = ppii; return (0); } static int note_zonename(struct ps_prochandle *P, size_t nbytes) { + core_info_t *core = P->data; char *zonename; - if (P->core->core_zonename != NULL) + if (core->core_zonename != NULL) return (0); /* Already seen */ if (nbytes != 0) { @@ -524,7 +724,7 @@ note_zonename(struct ps_prochandle *P, size_t nbytes) return (-1); } zonename[nbytes - 1] = '\0'; - P->core->core_zonename = zonename; + core->core_zonename = zonename; } return (0); @@ -536,7 +736,9 @@ note_auxv(struct ps_prochandle *P, size_t nbytes) size_t n, i; #ifdef _LP64 - if (P->core->core_dmodel == PR_MODEL_ILP32) { + core_info_t *core = P->data; + + if (core->core_dmodel == PR_MODEL_ILP32) { auxv32_t *a32; n = nbytes / sizeof (auxv32_t); @@ -594,7 +796,8 @@ note_auxv(struct ps_prochandle *P, size_t nbytes) static int note_xreg(struct ps_prochandle *P, size_t nbytes) { - lwp_info_t *lwp = P->core->core_lwp; + core_info_t *core = P->data; + lwp_info_t *lwp = core->core_lwp; size_t xbytes = sizeof (prxregset_t); prxregset_t *xregs; @@ -617,7 +820,8 @@ note_xreg(struct ps_prochandle *P, size_t nbytes) static int note_gwindows(struct ps_prochandle *P, size_t nbytes) { - lwp_info_t *lwp = P->core->core_lwp; + core_info_t *core = P->data; + lwp_info_t *lwp = core->core_lwp; if (lwp == NULL || lwp->lwp_gwins != NULL || nbytes == 0) return (0); /* No lwp yet or already seen or no data */ @@ -632,7 +836,7 @@ note_gwindows(struct ps_prochandle *P, size_t nbytes) * fails since we have to zero out gwindows first anyway. */ #ifdef _LP64 - if (P->core->core_dmodel == PR_MODEL_ILP32) { + if (core->core_dmodel == PR_MODEL_ILP32) { gwindows32_t g32; (void) memset(&g32, 0, sizeof (g32)); @@ -654,7 +858,8 @@ note_gwindows(struct ps_prochandle *P, size_t nbytes) static int note_asrs(struct ps_prochandle *P, size_t nbytes) { - lwp_info_t *lwp = P->core->core_lwp; + core_info_t *core = P->data; + lwp_info_t *lwp = core->core_lwp; int64_t *asrs; if (lwp == NULL || lwp->lwp_asrs != NULL || nbytes < sizeof (asrset_t)) @@ -679,7 +884,9 @@ static int note_spymaster(struct ps_prochandle *P, size_t nbytes) { #ifdef _LP64 - if (P->core->core_dmodel == PR_MODEL_ILP32) { + core_info_t *core = P->data; + + if (core->core_dmodel == PR_MODEL_ILP32) { psinfo32_t ps32; if (nbytes < sizeof (psinfo32_t) || @@ -830,6 +1037,7 @@ core_report_mapping(struct ps_prochandle *P, GElf_Phdr *php) static int core_add_mapping(struct ps_prochandle *P, GElf_Phdr *php) { + core_info_t *core = P->data; prmap_t pmap; dprintf("mapping base %llx filesz %llu memsz %llu offset %llu\n", @@ -845,7 +1053,7 @@ core_add_mapping(struct ps_prochandle *P, GElf_Phdr *php) */ if (php->p_flags & PF_SUNW_FAILURE) { core_report_mapping(P, php); - } else if (php->p_filesz != 0 && php->p_offset >= P->core->core_size) { + } else if (php->p_filesz != 0 && php->p_offset >= core->core_size) { Perror_printf(P, "core file may be corrupt -- data for mapping " "at %p is missing\n", (void *)(uintptr_t)php->p_vaddr); dprintf("core file may be corrupt -- data for mapping " @@ -1481,6 +1689,7 @@ core_find_data(struct ps_prochandle *P, Elf *elf, rd_loadobj_t *rlp) static int core_iter_mapping(const rd_loadobj_t *rlp, struct ps_prochandle *P) { + core_info_t *core = P->data; char lname[PATH_MAX], buf[PATH_MAX]; file_info_t *fp; map_info_t *mp; @@ -1508,7 +1717,7 @@ core_iter_mapping(const rd_loadobj_t *rlp, struct ps_prochandle *P) */ if ((fp = mp->map_file) == NULL && (fp = file_info_new(P, mp)) == NULL) { - P->core->core_errno = errno; + core->core_errno = errno; dprintf("failed to malloc mapping data\n"); return (0); /* Abort */ } @@ -1516,7 +1725,7 @@ core_iter_mapping(const rd_loadobj_t *rlp, struct ps_prochandle *P) /* Create a local copy of the load object representation */ if ((fp->file_lo = calloc(1, sizeof (rd_loadobj_t))) == NULL) { - P->core->core_errno = errno; + core->core_errno = errno; dprintf("failed to malloc mapping data\n"); return (0); /* Abort */ } @@ -1783,6 +1992,7 @@ struct ps_prochandle * Pfgrab_core(int core_fd, const char *aout_path, int *perr) { struct ps_prochandle *P; + core_info_t *core_info; map_info_t *stk_mp, *brk_mp; const char *execname; char *interp; @@ -1848,7 +2058,7 @@ Pfgrab_core(int core_fd, const char *aout_path, int *perr) P->agentstatfd = -1; P->zoneroot = NULL; P->info_valid = 1; - P->ops = &P_core_ops; + Pinit_ops(&P->ops, &P_core_ops); Pinitsym(P); @@ -1867,28 +2077,29 @@ Pfgrab_core(int core_fd, const char *aout_path, int *perr) * Allocate and initialize a core_info_t to hang off the ps_prochandle * structure. We keep all core-specific information in this structure. */ - if ((P->core = calloc(1, sizeof (core_info_t))) == NULL) { + if ((core_info = calloc(1, sizeof (core_info_t))) == NULL) { *perr = G_STRANGE; goto err; } - list_link(&P->core->core_lwp_head, NULL); - P->core->core_size = stbuf.st_size; + P->data = core_info; + list_link(&core_info->core_lwp_head, NULL); + core_info->core_size = stbuf.st_size; /* * In the days before adjustable core file content, this was the * default core file content. For new core files, this value will * be overwritten by the NT_CONTENT note section. */ - P->core->core_content = CC_CONTENT_STACK | CC_CONTENT_HEAP | + core_info->core_content = CC_CONTENT_STACK | CC_CONTENT_HEAP | CC_CONTENT_DATA | CC_CONTENT_RODATA | CC_CONTENT_ANON | CC_CONTENT_SHANON; switch (core.e_hdr.e_ident[EI_CLASS]) { case ELFCLASS32: - P->core->core_dmodel = PR_MODEL_ILP32; + core_info->core_dmodel = PR_MODEL_ILP32; break; case ELFCLASS64: - P->core->core_dmodel = PR_MODEL_LP64; + core_info->core_dmodel = PR_MODEL_LP64; break; default: *perr = G_FORMAT; @@ -2114,7 +2325,7 @@ Pfgrab_core(int core_fd, const char *aout_path, int *perr) interp = dp->d_buf; } else if (base_addr != (uintptr_t)-1L) { - if (P->core->core_dmodel == PR_MODEL_LP64) + if (core_info->core_dmodel == PR_MODEL_LP64) interp = "/usr/lib/64/ld.so.1"; else interp = "/usr/lib/ld.so.1"; @@ -2229,8 +2440,8 @@ Pfgrab_core(int core_fd, const char *aout_path, int *perr) (void) rd_loadobj_iter(P->rap, (rl_iter_f *) core_iter_mapping, P); - if (P->core->core_errno != 0) { - errno = P->core->core_errno; + if (core_info->core_errno != 0) { + errno = core_info->core_errno; *perr = G_STRANGE; goto err; } diff --git a/usr/src/lib/libproc/common/Pexecname.c b/usr/src/lib/libproc/common/Pexecname.c index 82b4219560..8af0416e10 100644 --- a/usr/src/lib/libproc/common/Pexecname.c +++ b/usr/src/lib/libproc/common/Pexecname.c @@ -22,6 +22,9 @@ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + */ #define __EXTENSIONS__ #include <string.h> @@ -236,23 +239,7 @@ found: } /* - * Callback function for Pfindexec(). We return a match if we can stat the - * suggested pathname and confirm its device and inode number match our - * previous information about the /proc/<pid>/object/a.out file. - */ -static int -stat_exec(const char *path, struct stat64 *stp) -{ - struct stat64 st; - - return (stat64(path, &st) == 0 && S_ISREG(st.st_mode) && - stp->st_dev == st.st_dev && stp->st_ino == st.st_ino); -} - -/* - * Return the full pathname for the executable file. If the process handle is - * a core file, we've already tried our best to get the executable name. - * Otherwise, we make an attempt using Pfindexec(). + * Return the full pathname for the executable file. */ char * Pexecname(struct ps_prochandle *P, char *buf, size_t buflen) @@ -262,48 +249,5 @@ Pexecname(struct ps_prochandle *P, char *buf, size_t buflen) return (buf); } - if (P->state != PS_DEAD && P->state != PS_IDLE) { - char exec_name[PATH_MAX]; - char cwd[PATH_MAX]; - char proc_cwd[64]; - struct stat64 st; - int ret; - - /* - * Try to get the path information first. - */ - (void) snprintf(exec_name, sizeof (exec_name), - "%s/%d/path/a.out", procfs_path, (int)P->pid); - if ((ret = readlink(exec_name, buf, buflen - 1)) > 0) { - buf[ret] = '\0'; - (void) Pfindobj(P, buf, buf, buflen); - return (buf); - } - - /* - * Stat the executable file so we can compare Pfindexec's - * suggestions to the actual device and inode number. - */ - (void) snprintf(exec_name, sizeof (exec_name), - "%s/%d/object/a.out", procfs_path, (int)P->pid); - - if (stat64(exec_name, &st) != 0 || !S_ISREG(st.st_mode)) - return (NULL); - - /* - * Attempt to figure out the current working directory of the - * target process. This only works if the target process has - * not changed its current directory since it was exec'd. - */ - (void) snprintf(proc_cwd, sizeof (proc_cwd), - "%s/%d/path/cwd", procfs_path, (int)P->pid); - - if ((ret = readlink(proc_cwd, cwd, PATH_MAX - 1)) > 0) - cwd[ret] = '\0'; - - (void) Pfindexec(P, ret > 0 ? cwd : NULL, - (int (*)(const char *, void *))stat_exec, &st); - } - - return (NULL); + return (P->ops.pop_execname(P, buf, buflen, P->data)); } diff --git a/usr/src/lib/libproc/common/Pgcore.c b/usr/src/lib/libproc/common/Pgcore.c index 2b5ec7c9bd..bf530be90c 100644 --- a/usr/src/lib/libproc/common/Pgcore.c +++ b/usr/src/lib/libproc/common/Pgcore.c @@ -26,6 +26,7 @@ /* * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. */ #define _STRUCTURED_PROC 1 @@ -1357,11 +1358,11 @@ Pfgcore(struct ps_prochandle *P, int fd, core_content_t content) } { - prpriv_t *ppriv; + prpriv_t *ppriv = NULL; const priv_impl_info_t *pinfo; size_t pprivsz, pinfosz; - if ((ppriv = proc_get_priv(P->pid)) == NULL) + if (Ppriv(P, &ppriv) == -1) goto err; pprivsz = PRIV_PRPRIV_SIZE(ppriv); diff --git a/usr/src/lib/libproc/common/Pidle.c b/usr/src/lib/libproc/common/Pidle.c index 938510abeb..3191f4fa7e 100644 --- a/usr/src/lib/libproc/common/Pidle.c +++ b/usr/src/lib/libproc/common/Pidle.c @@ -22,6 +22,9 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + */ #include <stdlib.h> #include <libelf.h> @@ -34,8 +37,10 @@ #include "libproc.h" #include "Pcontrol.h" +/*ARGSUSED*/ static ssize_t -Pread_idle(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr) +Pread_idle(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr, + void *data) { size_t resid = n; @@ -65,15 +70,55 @@ Pread_idle(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr) /*ARGSUSED*/ static ssize_t -Pwrite_idle(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr) +Pwrite_idle(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr, + void *data) { errno = EIO; return (-1); } -static const ps_rwops_t P_idle_ops = { - Pread_idle, - Pwrite_idle +/*ARGSUSED*/ +static int +Ppriv_idle(struct ps_prochandle *P, prpriv_t **pprv, void *data) +{ + prpriv_t *pp; + + pp = proc_get_priv(P->pid); + if (pp == NULL) { + return (-1); + } + + *pprv = pp; + return (0); +} + +/* Default operations for the idl ops vector. */ +static void * +Pidle_voidp() +{ + errno = ENODATA; + return (NULL); +} + +static int +Pidle_int() +{ + errno = ENODATA; + return (-1); +} + +static const ps_ops_t P_idle_ops = { + .pop_pread = Pread_idle, + .pop_pwrite = Pwrite_idle, + .pop_cred = (pop_cred_t)Pidle_int, + .pop_priv = Ppriv_idle, + .pop_psinfo = (pop_psinfo_t)Pidle_voidp, + .pop_platform = (pop_platform_t)Pidle_voidp, + .pop_uname = (pop_uname_t)Pidle_int, + .pop_zonename = (pop_zonename_t)Pidle_voidp, +#if defined(__i386) || defined(__amd64) + .pop_ldt = (pop_ldt_t)Pidle_int +#endif }; static int @@ -143,7 +188,7 @@ Pgrab_file(const char *fname, int *perr) P->agentctlfd = -1; P->agentstatfd = -1; P->info_valid = -1; - P->ops = &P_idle_ops; + Pinit_ops(&P->ops, &P_idle_ops); Pinitsym(P); if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { diff --git a/usr/src/lib/libproc/common/Plwpregs.c b/usr/src/lib/libproc/common/Plwpregs.c index 7549bc142d..c2b150000f 100644 --- a/usr/src/lib/libproc/common/Plwpregs.c +++ b/usr/src/lib/libproc/common/Plwpregs.c @@ -25,6 +25,7 @@ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. */ #include <sys/types.h> @@ -50,10 +51,11 @@ static lwp_info_t * getlwpcore(struct ps_prochandle *P, lwpid_t lwpid) { - lwp_info_t *lwp = list_next(&P->core->core_lwp_head); + core_info_t *core = P->data; + lwp_info_t *lwp = list_next(&core->core_lwp_head); uint_t i; - for (i = 0; i < P->core->core_nlwp; i++, lwp = list_next(lwp)) { + for (i = 0; i < core->core_nlwp; i++, lwp = list_next(lwp)) { if (lwp->lwp_id == lwpid) return (lwp); } @@ -118,7 +120,7 @@ getlwpstatus(struct ps_prochandle *P, lwpid_t lwpid, lwpstatus_t *lps) * If this is a core file, we need to iterate through our list of * cached lwp information and then copy out the status. */ - if (P->core != NULL && (lwp = getlwpcore(P, lwpid)) != NULL) { + if (P->data != NULL && (lwp = getlwpcore(P, lwpid)) != NULL) { (void) memcpy(lps, &lwp->lwp_status, sizeof (lwpstatus_t)); return (0); } diff --git a/usr/src/lib/libproc/common/Pservice.c b/usr/src/lib/libproc/common/Pservice.c index cd43947171..62c88d3ec8 100644 --- a/usr/src/lib/libproc/common/Pservice.c +++ b/usr/src/lib/libproc/common/Pservice.c @@ -22,8 +22,9 @@ * Copyright 2006 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 <stdarg.h> #include <string.h> @@ -56,7 +57,7 @@ ps_pdmodel(struct ps_prochandle *P, int *modelp) ps_err_e ps_pread(struct ps_prochandle *P, psaddr_t addr, void *buf, size_t size) { - if (P->ops->p_pread(P, buf, size, addr) != size) + if (P->ops.pop_pread(P, buf, size, addr, P->data) != size) return (PS_BADADDR); return (PS_OK); } @@ -64,7 +65,7 @@ ps_pread(struct ps_prochandle *P, psaddr_t addr, void *buf, size_t size) ps_err_e ps_pwrite(struct ps_prochandle *P, psaddr_t addr, const void *buf, size_t size) { - if (P->ops->p_pwrite(P, buf, size, addr) != size) + if (P->ops.pop_pwrite(P, buf, size, addr, P->data) != size) return (PS_BADADDR); return (PS_OK); } diff --git a/usr/src/lib/libproc/common/Psymtab.c b/usr/src/lib/libproc/common/Psymtab.c index 7c8166d5c3..62354f9a7b 100644 --- a/usr/src/lib/libproc/common/Psymtab.c +++ b/usr/src/lib/libproc/common/Psymtab.c @@ -22,6 +22,7 @@ /* * Copyright (c) 1997, 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 <assert.h> @@ -41,7 +42,6 @@ #include <libgen.h> #include <sys/types.h> #include <sys/stat.h> -#include <sys/systeminfo.h> #include <sys/sysmacros.h> #include "libproc.h" @@ -434,6 +434,12 @@ load_static_maps(struct ps_prochandle *P) map_set(P, mptr, "ld.so.1"); } +int +Preadmaps(struct ps_prochandle *P, prmap_t **Pmapp, ssize_t *nmapp) +{ + return (P->ops.pop_read_maps(P, Pmapp, nmapp, P->data)); +} + /* * Go through all the address space mappings, validating or updating * the information already gathered, or gathering new information. @@ -444,9 +450,6 @@ load_static_maps(struct ps_prochandle *P) void Pupdate_maps(struct ps_prochandle *P) { - char mapfile[PATH_MAX]; - int mapfd; - struct stat statb; prmap_t *Pmap = NULL; prmap_t *pmap; ssize_t nmap; @@ -460,22 +463,8 @@ Pupdate_maps(struct ps_prochandle *P) Preadauxvec(P); - (void) snprintf(mapfile, sizeof (mapfile), "%s/%d/map", - procfs_path, (int)P->pid); - if ((mapfd = open(mapfile, O_RDONLY)) < 0 || - fstat(mapfd, &statb) != 0 || - statb.st_size < sizeof (prmap_t) || - (Pmap = malloc(statb.st_size)) == NULL || - (nmap = pread(mapfd, Pmap, statb.st_size, 0L)) <= 0 || - (nmap /= sizeof (prmap_t)) == 0) { - if (Pmap != NULL) - free(Pmap); - if (mapfd >= 0) - (void) close(mapfd); - Preset_maps(P); /* utter failure; destroy tables */ + if (Preadmaps(P, &Pmap, &nmap) != 0) return; - } - (void) close(mapfd); if ((newmap = calloc(1, nmap * sizeof (map_info_t))) == NULL) return; @@ -848,51 +837,16 @@ Pname_to_ctf(struct ps_prochandle *P, const char *name) return (Plmid_to_ctf(P, PR_LMID_EVERY, name)); } -/* - * If we're not a core file, re-read the /proc/<pid>/auxv file and store - * its contents in P->auxv. In the case of a core file, we either - * initialized P->auxv in Pcore() from the NT_AUXV, or we don't have an - * auxv because the note was missing. - */ void Preadauxvec(struct ps_prochandle *P) { - char auxfile[64]; - struct stat statb; - ssize_t naux; - int fd; - - if (P->state == PS_DEAD) - return; /* Already read during Pgrab_core() */ - if (P->state == PS_IDLE) - return; /* No aux vec for Pgrab_file() */ - if (P->auxv != NULL) { free(P->auxv); P->auxv = NULL; P->nauxv = 0; } - (void) snprintf(auxfile, sizeof (auxfile), "%s/%d/auxv", - procfs_path, (int)P->pid); - if ((fd = open(auxfile, O_RDONLY)) < 0) - return; - - if (fstat(fd, &statb) == 0 && - statb.st_size >= sizeof (auxv_t) && - (P->auxv = malloc(statb.st_size + sizeof (auxv_t))) != NULL) { - if ((naux = read(fd, P->auxv, statb.st_size)) < 0 || - (naux /= sizeof (auxv_t)) < 1) { - free(P->auxv); - P->auxv = NULL; - } else { - P->auxv[naux].a_type = AT_NULL; - P->auxv[naux].a_un.a_val = 0L; - P->nauxv = (int)naux; - } - } - - (void) close(fd); + P->ops.pop_read_aux(P, &P->auxv, &P->nauxv, P->data); } /* @@ -1683,7 +1637,7 @@ Pbuild_file_symtab(struct ps_prochandle *P, file_info_t *fptr) * the in-core elf image. */ - if (_libproc_incore_elf) { + if (_libproc_incore_elf || (P->flags & INCORE)) { dprintf("Pbuild_file_symtab: using in-core data for: %s\n", fptr->file_pname); @@ -2969,52 +2923,21 @@ Psymbol_iter_by_name(struct ps_prochandle *P, } /* - * Get the platform string from the core file if we have it; - * just perform the system call for the caller if this is a live process. + * Get the platform string. */ char * Pplatform(struct ps_prochandle *P, char *s, size_t n) { - if (P->state == PS_IDLE) { - errno = ENODATA; - return (NULL); - } - - if (P->state == PS_DEAD) { - if (P->core->core_platform == NULL) { - errno = ENODATA; - return (NULL); - } - (void) strncpy(s, P->core->core_platform, n - 1); - s[n - 1] = '\0'; - - } else if (sysinfo(SI_PLATFORM, s, n) == -1) - return (NULL); - - return (s); + return (P->ops.pop_platform(P, s, n, P->data)); } /* - * Get the uname(2) information from the core file if we have it; - * just perform the system call for the caller if this is a live process. + * Get the uname(2) information. */ int Puname(struct ps_prochandle *P, struct utsname *u) { - if (P->state == PS_IDLE) { - errno = ENODATA; - return (-1); - } - - if (P->state == PS_DEAD) { - if (P->core->core_uts == NULL) { - errno = ENODATA; - return (-1); - } - (void) memcpy(u, P->core->core_uts, sizeof (struct utsname)); - return (0); - } - return (uname(u)); + return (P->ops.pop_uname(P, u, P->data)); } /* diff --git a/usr/src/lib/libproc/common/Putil.c b/usr/src/lib/libproc/common/Putil.c index 791ec668cb..f6f2aa862e 100644 --- a/usr/src/lib/libproc/common/Putil.c +++ b/usr/src/lib/libproc/common/Putil.c @@ -23,8 +23,9 @@ * Copyright 2005 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 <limits.h> #include <string.h> @@ -150,3 +151,96 @@ Perror_printf(struct ps_prochandle *P, const char *format, ...) { /* nothing to do here */ } + +/* + * Default operations. + */ +static ssize_t +Pdefault_ssizet() +{ + return (-1); +} + +static int +Pdefault_int() +{ + return (-1); +} + +static void +Pdefault_void() +{ +} + +static void * +Pdefault_voidp() +{ + return (NULL); +} + +static const ps_ops_t P_default_ops = { + .pop_pread = (pop_pread_t)Pdefault_ssizet, + .pop_pwrite = (pop_pwrite_t)Pdefault_ssizet, + .pop_read_maps = (pop_read_maps_t)Pdefault_int, + .pop_read_aux = (pop_read_aux_t)Pdefault_void, + .pop_cred = (pop_cred_t)Pdefault_int, + .pop_priv = (pop_priv_t)Pdefault_int, + .pop_psinfo = (pop_psinfo_t)Pdefault_voidp, + .pop_status = (pop_status_t)Pdefault_void, + .pop_lstatus = (pop_lstatus_t)Pdefault_voidp, + .pop_lpsinfo = (pop_lpsinfo_t)Pdefault_voidp, + .pop_fini = (pop_fini_t)Pdefault_void, + .pop_platform = (pop_platform_t)Pdefault_voidp, + .pop_uname = (pop_uname_t)Pdefault_int, + .pop_zonename = (pop_zonename_t)Pdefault_voidp, + .pop_execname = (pop_execname_t)Pdefault_voidp, +#if defined(__i386) || defined(__amd64) + .pop_ldt = (pop_ldt_t)Pdefault_int +#endif +}; + +/* + * Initialize the destination ops vector with functions from the source. + * Functions which are NULL in the source ops vector are set to corresponding + * default function in the destination vector. + */ +void +Pinit_ops(ps_ops_t *dst, const ps_ops_t *src) +{ + *dst = P_default_ops; + + if (src->pop_pread != NULL) + dst->pop_pread = src->pop_pread; + if (src->pop_pwrite != NULL) + dst->pop_pwrite = src->pop_pwrite; + if (src->pop_read_maps != NULL) + dst->pop_read_maps = src->pop_read_maps; + if (src->pop_read_aux != NULL) + dst->pop_read_aux = src->pop_read_aux; + if (src->pop_cred != NULL) + dst->pop_cred = src->pop_cred; + if (src->pop_priv != NULL) + dst->pop_priv = src->pop_priv; + if (src->pop_psinfo != NULL) + dst->pop_psinfo = src->pop_psinfo; + if (src->pop_status != NULL) + dst->pop_status = src->pop_status; + if (src->pop_lstatus != NULL) + dst->pop_lstatus = src->pop_lstatus; + if (src->pop_lpsinfo != NULL) + dst->pop_lpsinfo = src->pop_lpsinfo; + if (src->pop_fini != NULL) + dst->pop_fini = src->pop_fini; + if (src->pop_platform != NULL) + dst->pop_platform = src->pop_platform; + if (src->pop_uname != NULL) + dst->pop_uname = src->pop_uname; + if (src->pop_zonename != NULL) + dst->pop_zonename = src->pop_zonename; + if (src->pop_execname != NULL) + dst->pop_execname = src->pop_execname; +#if defined(__i386) || defined(__amd64) + if (src->pop_ldt != NULL) + dst->pop_ldt = src->pop_ldt; +#endif +} diff --git a/usr/src/lib/libproc/common/Putil.h b/usr/src/lib/libproc/common/Putil.h index 55ea45dba2..57814a9a7f 100644 --- a/usr/src/lib/libproc/common/Putil.h +++ b/usr/src/lib/libproc/common/Putil.h @@ -23,12 +23,13 @@ * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + */ #ifndef _PUTIL_H #define _PUTIL_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -64,6 +65,8 @@ extern int prset_ismember(void *, size_t, uint_t); */ extern void dprintf(const char *, ...); +extern void Pinit_ops(ps_ops_t *, const ps_ops_t *); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libproc/common/Pzone.c b/usr/src/lib/libproc/common/Pzone.c index 68efea7a72..67a8c0b3c9 100644 --- a/usr/src/lib/libproc/common/Pzone.c +++ b/usr/src/lib/libproc/common/Pzone.c @@ -25,6 +25,7 @@ */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. */ #include <assert.h> @@ -231,23 +232,7 @@ Pbrandname(struct ps_prochandle *P, char *buf, size_t buflen) char * Pzonename(struct ps_prochandle *P, char *s, size_t n) { - if (P->state == PS_IDLE) { - errno = ENODATA; - return (NULL); - } - - if (P->state == PS_DEAD) { - if (P->core->core_zonename == NULL) { - errno = ENODATA; - return (NULL); - } - (void) strlcpy(s, P->core->core_zonename, n); - } else { - if (getzonenamebyid(P->status.pr_zoneid, s, n) < 0) - return (NULL); - s[n - 1] = '\0'; - } - return (s); + return (P->ops.pop_zonename(P, s, n, P->data)); } char * diff --git a/usr/src/lib/libproc/common/libproc.h b/usr/src/lib/libproc/common/libproc.h index 7cfc80cd7a..01970f439e 100644 --- a/usr/src/lib/libproc/common/libproc.h +++ b/usr/src/lib/libproc/common/libproc.h @@ -26,6 +26,7 @@ * Portions Copyright 2007 Chad Mynhier * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. */ /* @@ -127,6 +128,7 @@ extern int _libproc_incore_elf; /* only use in-core elf data */ #define PGRAB_FORCE 0x02 /* Open the process w/o O_EXCL */ #define PGRAB_RDONLY 0x04 /* Open the process or core w/ O_RDONLY */ #define PGRAB_NOSTOP 0x08 /* Open the process but do not stop it */ +#define PGRAB_INCORE 0x10 /* Use in-core data to build symbol tables */ /* Error codes from Pcreate() */ #define C_STRANGE -1 /* Unanticipated error, errno is meaningful */ @@ -188,6 +190,55 @@ typedef struct { /* argument descriptor for system call (Psyscall) */ #define MAXARGL (4*1024) /* + * Ops vector definition for the Pgrab_ops(). + */ +typedef ssize_t (*pop_pread_t)(struct ps_prochandle *, void *, size_t, + uintptr_t, void *); +typedef ssize_t (*pop_pwrite_t)(struct ps_prochandle *, const void *, size_t, + uintptr_t, void *); +typedef int (*pop_read_maps_t)(struct ps_prochandle *, prmap_t **, ssize_t *, + void *); +typedef void (*pop_read_aux_t)(struct ps_prochandle *, auxv_t **, int *, + void *); +typedef int (*pop_cred_t)(struct ps_prochandle *, prcred_t *, int, + void *); +typedef int (*pop_priv_t)(struct ps_prochandle *, prpriv_t **, void *); +typedef const psinfo_t *(*pop_psinfo_t)(struct ps_prochandle *, psinfo_t *, + void *); +typedef void (*pop_status_t)(struct ps_prochandle *, pstatus_t *, void *); +typedef prheader_t *(*pop_lstatus_t)(struct ps_prochandle *, void *); +typedef prheader_t *(*pop_lpsinfo_t)(struct ps_prochandle *, void *); +typedef void (*pop_fini_t)(struct ps_prochandle *, void *); +typedef char *(*pop_platform_t)(struct ps_prochandle *, char *, size_t, void *); +typedef int (*pop_uname_t)(struct ps_prochandle *, struct utsname *, void *); +typedef char *(*pop_zonename_t)(struct ps_prochandle *, char *, size_t, void *); +typedef char *(*pop_execname_t)(struct ps_prochandle *, char *, size_t, void *); +#if defined(__i386) || defined(__amd64) +typedef int (*pop_ldt_t)(struct ps_prochandle *, struct ssd *, int, void *); +#endif + +typedef struct ps_ops { + pop_pread_t pop_pread; + pop_pwrite_t pop_pwrite; + pop_read_maps_t pop_read_maps; + pop_read_aux_t pop_read_aux; + pop_cred_t pop_cred; + pop_priv_t pop_priv; + pop_psinfo_t pop_psinfo; + pop_status_t pop_status; + pop_lstatus_t pop_lstatus; + pop_lpsinfo_t pop_lpsinfo; + pop_fini_t pop_fini; + pop_platform_t pop_platform; + pop_uname_t pop_uname; + pop_zonename_t pop_zonename; + pop_execname_t pop_execname; +#if defined(__i386) || defined(__amd64) + pop_ldt_t pop_ldt; +#endif +} ps_ops_t; + +/* * Function prototypes for routines in the process control package. */ extern struct ps_prochandle *Pcreate(const char *, char *const *, @@ -201,6 +252,7 @@ extern struct ps_prochandle *Pgrab(pid_t, int, int *); extern struct ps_prochandle *Pgrab_core(const char *, const char *, int, int *); extern struct ps_prochandle *Pfgrab_core(int, const char *, int *); extern struct ps_prochandle *Pgrab_file(const char *, int *); +extern struct ps_prochandle *Pgrab_ops(pid_t, void *, const ps_ops_t *, int); extern const char *Pgrab_error(int); extern int Preopen(struct ps_prochandle *); @@ -221,7 +273,7 @@ extern const psinfo_t *Ppsinfo(struct ps_prochandle *); extern const pstatus_t *Pstatus(struct ps_prochandle *); extern int Pcred(struct ps_prochandle *, prcred_t *, int); extern int Psetcred(struct ps_prochandle *, const prcred_t *); -extern ssize_t Ppriv(struct ps_prochandle *, prpriv_t *, size_t); +extern int Ppriv(struct ps_prochandle *, prpriv_t **); extern int Psetpriv(struct ps_prochandle *, prpriv_t *); extern void *Pprivinfo(struct ps_prochandle *); extern int Psetzoneid(struct ps_prochandle *, zoneid_t); diff --git a/usr/src/lib/libproc/common/llib-lproc b/usr/src/lib/libproc/common/llib-lproc index 9626e80024..35907f04d0 100644 --- a/usr/src/lib/libproc/common/llib-lproc +++ b/usr/src/lib/libproc/common/llib-lproc @@ -25,6 +25,9 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + */ #include "libproc.h" /* @@ -46,7 +49,7 @@ int Pctlfd(struct ps_prochandle *Pr); const psinfo_t *Ppsinfo(struct ps_prochandle *Pr); const pstatus_t *Pstatus(struct ps_prochandle *Pr); int Pcred(struct ps_prochandle *Pr, prcred_t *pcrp, int ngroups); -ssize_t Ppriv(struct ps_prochandle *Pr, prpriv_t *pprivp, size_t); +int Ppriv(struct ps_prochandle *Pr, prpriv_t **pprivp); void Psync(struct ps_prochandle *Pr); int Pcreate_agent(struct ps_prochandle *Pr); void Pdestroy_agent(struct ps_prochandle *Pr); diff --git a/usr/src/lib/libproc/common/mapfile-vers b/usr/src/lib/libproc/common/mapfile-vers index eb91461d98..666d63fbf0 100644 --- a/usr/src/lib/libproc/common/mapfile-vers +++ b/usr/src/lib/libproc/common/mapfile-vers @@ -22,6 +22,7 @@ # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2012 DEY Storage Systems, Inc. All rights reserved. # Copyright (c) 2013, Joyent, Inc. All rights reserved. +# Copyright (c) 2013 by Delphix. All rights reserved. # # @@ -123,6 +124,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { Pgrab_core; Pgrab_error; Pgrab_file; + Pgrab_ops; Pisprocdir; Pissyscall_prev; Plmid; diff --git a/usr/src/uts/common/sys/lwp.h b/usr/src/uts/common/sys/lwp.h index 1333225e65..03081022b3 100644 --- a/usr/src/uts/common/sys/lwp.h +++ b/usr/src/uts/common/sys/lwp.h @@ -23,12 +23,13 @@ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + */ #ifndef _SYS_LWP_H #define _SYS_LWP_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/synch.h> #include <sys/ucontext.h> @@ -64,6 +65,8 @@ struct lwpinfo32 { #endif /* _SYSCALL32 */ +typedef uint_t lwpid_t; + #define _LWP_FSBASE 0 #define _LWP_GSBASE 1 @@ -72,8 +75,6 @@ struct lwpinfo32 { #ifndef _KERNEL -typedef uint_t lwpid_t; - int _lwp_kill(lwpid_t, int); int _lwp_info(struct lwpinfo *); lwpid_t _lwp_self(void); diff --git a/usr/src/uts/common/sys/syscall.h b/usr/src/uts/common/sys/syscall.h index 633bfef143..15dc00aa45 100644 --- a/usr/src/uts/common/sys/syscall.h +++ b/usr/src/uts/common/sys/syscall.h @@ -21,6 +21,7 @@ /* * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -498,13 +499,13 @@ typedef struct { /* syscall set type */ unsigned int word[16]; } sysset_t; -#if !defined(_KERNEL) - typedef struct { /* return values from system call */ long sys_rval1; /* primary return value from system call */ long sys_rval2; /* second return value from system call */ } sysret_t; +#if !defined(_KERNEL) + #if defined(__STDC__) extern int syscall(int, ...); extern int __systemcall(sysret_t *, int, ...); |