changeset 14155:dcd9e8748b08

3946 ::gcore Reviewed by: Adam Leventhal <ahl@delphix.com> Reviewed by: Matthew Ahrens <mahrens@delphix.com> Approved by: Robert Mustacchi <rm@joyent.com>
author Jeremy Jones <jeremy@delphix.com>
date Wed, 21 Aug 2013 15:45:46 -0800
parents 4c227138a4c3
children 5dce77d754b7
files usr/src/cmd/mdb/Makefile.module usr/src/cmd/mdb/common/mdb/mdb_modapi.c usr/src/cmd/mdb/common/mdb/mdb_modapi.h usr/src/cmd/mdb/common/modules/conf/mapfile-extern usr/src/cmd/mdb/common/modules/genunix/Makefile.files usr/src/cmd/mdb/common/modules/genunix/avl.c usr/src/cmd/mdb/common/modules/genunix/avl.h usr/src/cmd/mdb/common/modules/genunix/gcore.c usr/src/cmd/mdb/common/modules/genunix/gcore.h usr/src/cmd/mdb/common/modules/genunix/genunix.c usr/src/cmd/mdb/intel/amd64/genunix/Makefile usr/src/cmd/mdb/intel/ia32/genunix/Makefile usr/src/cmd/ptools/ppriv/ppriv.c usr/src/lib/libproc/Makefile.com usr/src/lib/libproc/common/Pcontrol.c usr/src/lib/libproc/common/Pcontrol.h usr/src/lib/libproc/common/Pcore.c usr/src/lib/libproc/common/Pexecname.c usr/src/lib/libproc/common/Pgcore.c usr/src/lib/libproc/common/Pidle.c usr/src/lib/libproc/common/Plwpregs.c usr/src/lib/libproc/common/Pservice.c usr/src/lib/libproc/common/Psymtab.c usr/src/lib/libproc/common/Putil.c usr/src/lib/libproc/common/Putil.h usr/src/lib/libproc/common/Pzone.c usr/src/lib/libproc/common/libproc.h usr/src/lib/libproc/common/llib-lproc usr/src/lib/libproc/common/mapfile-vers usr/src/uts/common/sys/lwp.h usr/src/uts/common/sys/syscall.h
diffstat 31 files changed, 3388 insertions(+), 422 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/mdb/Makefile.module	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/cmd/mdb/Makefile.module	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 kvm_TGTFLAGS		= -D_KERNEL
 proc_TGTFLAGS		= -D_USER
 
+C99MODE			= $(C99_ENABLE)
+
 CFLAGS			+= $(CCVERBOSE)
 CFLAGS64		+= $(CCVERBOSE)
 CPPFLAGS		+= $($(MDBTGT)_TGTFLAGS) -I../../../common
--- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.c	Wed Aug 21 15:45:46 2013 -0800
@@ -71,6 +71,23 @@
 }
 
 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);
--- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h	Wed Aug 21 15:45:46 2013 -0800
@@ -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 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);
 
--- a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 #		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 };
--- a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 	findstack.c	\
 	findstack_subr.c \
 	fm.c		\
+	gcore.c		\
 	genunix.c	\
 	group.c		\
 	hotplug.c	\
--- a/usr/src/cmd/mdb/common/modules/genunix/avl.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/cmd/mdb/common/modules/genunix/avl.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 
 	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);
+}
--- a/usr/src/cmd/mdb/common/modules/genunix/avl.h	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/cmd/mdb/common/modules/genunix/avl.h	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
     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
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/genunix/gcore.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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, "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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/genunix/gcore.h	Wed Aug 21 15:45:46 2013 -0800
@@ -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 */
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 	{ "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 @@
 	(void) mdb_callback_add(MDB_CALLBACK_STCHG,
 	    genunix_statechange_cb, NULL);
 
+#ifndef _KMDB
+	gcore_init();
+#endif
+
 	return (&modinfo);
 }
 
--- a/usr/src/cmd/mdb/intel/amd64/genunix/Makefile	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/cmd/mdb/intel/amd64/genunix/Makefile	Wed Aug 21 15:45:46 2013 -0800
@@ -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.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-uninitialized
 CERRWARN += -_gcc=-Wno-parentheses
 CERRWARN += -_gcc=-Wno-type-limits
+
+LINTFLAGS64 += -erroff=E_EMPTY_TRANSLATION_UNIT
--- a/usr/src/cmd/mdb/intel/ia32/genunix/Makefile	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/cmd/mdb/intel/ia32/genunix/Makefile	Wed Aug 21 15:45:46 2013 -0800
@@ -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.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-uninitialized
 CERRWARN += -_gcc=-Wno-parentheses
 CERRWARN += -_gcc=-Wno-type-limits
+
+LINTFLAGS += -erroff=E_EMPTY_TRANSLATION_UNIT
--- a/usr/src/cmd/ptools/ppriv/ppriv.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/cmd/ptools/ppriv/ppriv.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 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 @@
 	char *x;
 	int i;
 	boolean_t nodata;
+	prpriv_t *ppriv;
 
 	procname = arg;		/* for perr() */
 
@@ -179,15 +181,11 @@
 		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 @@
 		    "%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 @@
 		}
 	}
 	Prelease(Pr, 0);
+	free(ppriv);
 	return (0);
 }
 
--- a/usr/src/lib/libproc/Makefile.com	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/Makefile.com	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 
 LIBS =		$(DYNLIB) $(LINTLIB)
 LDLIBS +=	-lrtld_db -lelf -lctf -lc
+C99MODE =	$(C99_ENABLE)
 CPPFLAGS +=	$($(MACH64)_CPPFLAGS)
 
 SRCDIR =	../common
--- a/usr/src/lib/libproc/common/Pcontrol.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/Pcontrol.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 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 @@
 	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 @@
 	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 @@
 {
 	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 @@
 	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 @@
 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 @@
 	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 @@
 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 (0);
-	}
-
-	errno = ENODATA;
-	return (-1);
+	return (P->ops.pop_cred(P, pcrp, ngroups, P->data));
+}
+
+static prheader_t *
+Plstatus(struct ps_prochandle *P)
+{
+	return (P->ops.pop_lstatus(P, P->data));
 }
 
+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 @@
 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);
-
-		(void) memcpy(pldt, P->core->core_ldt,
-		    nldt * sizeof (struct ssd));
-
-		return (nldt);
-	}
-
-	errno = ENODATA;
-	return (-1);
+	return (P->ops.pop_ldt(P, pldt, nldt, P->data));
+
 }
 #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)
+int
+Ppriv(struct ps_prochandle *P, prpriv_t **pprv)
 {
-	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);
+	return (P->ops.pop_priv(P, pprv, P->data));
 }
 
 int
@@ -1214,11 +1366,13 @@
 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 @@
 	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 @@
 	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 @@
 	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 @@
 	 * 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 @@
 	 * 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 @@
 	 * 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 @@
 	}
 
 	/*
-	 * 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 @@
 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 @@
 		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);
+}
--- a/usr/src/lib/libproc/common/Pcontrol.h	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/Pcontrol.h	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 	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 @@
 	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 @@
 	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 @@
 #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 */
--- a/usr/src/lib/libproc/common/Pcore.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/Pcore.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 	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 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 @@
 	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 @@
 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 @@
 	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 @@
 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 @@
 	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 @@
 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 @@
 			return (-1);
 		}
 		plat[nbytes - 1] = '\0';
-		P->core->core_platform = plat;
+		core->core_platform = plat;
 	}
 
 	return (0);
@@ -349,10 +542,11 @@
 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 @@
 		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 @@
 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 @@
 	 * 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 @@
 		pcrp->pr_ngroups = ngroups;
 	}
 
-	P->core->core_cred = pcrp;
+	core->core_cred = pcrp;
 	return (0);
 }
 
@@ -436,10 +632,11 @@
 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 @@
 		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 @@
 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 @@
 		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 @@
 		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 @@
 			return (-1);
 		}
 		zonename[nbytes - 1] = '\0';
-		P->core->core_zonename = zonename;
+		core->core_zonename = zonename;
 	}
 
 	return (0);
@@ -536,7 +736,9 @@
 	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 @@
 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 @@
 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 @@
 	 * 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 @@
 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 @@
 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 @@
 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 @@
 	 */
 	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 @@
 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 @@
 	 */
 	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 @@
 
 	/* 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 @@
 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 @@
 	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 @@
 	 * 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 @@
 		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 @@
 		(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;
 		}
--- a/usr/src/lib/libproc/common/Pexecname.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/Pexecname.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 }
 
 /*
- * 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 @@
 		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));
 }
--- a/usr/src/lib/libproc/common/Pgcore.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/Pgcore.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 	}
 
 	{
-		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);
 
--- a/usr/src/lib/libproc/common/Pidle.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/Pidle.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 
 /*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 @@
 	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) {
--- a/usr/src/lib/libproc/common/Plwpregs.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/Plwpregs.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 	 * 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);
 	}
--- a/usr/src/lib/libproc/common/Pservice.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/Pservice.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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_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_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);
 }
--- a/usr/src/lib/libproc/common/Psymtab.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/Psymtab.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 		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 @@
 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 @@
 
 	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 @@
 	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 @@
 	 * 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 @@
 }
 
 /*
- * 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));
 }
 
 /*
--- a/usr/src/lib/libproc/common/Putil.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/Putil.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 {
 	/* 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
+}
--- a/usr/src/lib/libproc/common/Putil.h	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/Putil.h	Wed Aug 21 15:45:46 2013 -0800
@@ -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 void dprintf(const char *, ...);
 
+extern void Pinit_ops(ps_ops_t *, const ps_ops_t *);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/lib/libproc/common/Pzone.c	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/Pzone.c	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 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 *
--- a/usr/src/lib/libproc/common/libproc.h	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/libproc.h	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 #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 @@
 #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_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 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);
--- a/usr/src/lib/libproc/common/llib-lproc	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/llib-lproc	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 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);
--- a/usr/src/lib/libproc/common/mapfile-vers	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/lib/libproc/common/mapfile-vers	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 	Pgrab_core;
 	Pgrab_error;
 	Pgrab_file;
+	Pgrab_ops;
 	Pisprocdir;
 	Pissyscall_prev;
 	Plmid;
--- a/usr/src/uts/common/sys/lwp.h	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/uts/common/sys/lwp.h	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 
 #endif	/* _SYSCALL32 */
 
+typedef uint_t lwpid_t;
+
 #define	_LWP_FSBASE	0
 #define	_LWP_GSBASE	1
 
@@ -72,8 +75,6 @@
 
 #ifndef _KERNEL
 
-typedef uint_t lwpid_t;
-
 int		_lwp_kill(lwpid_t, int);
 int		_lwp_info(struct lwpinfo *);
 lwpid_t		_lwp_self(void);
--- a/usr/src/uts/common/sys/syscall.h	Wed Aug 21 13:46:56 2013 -0800
+++ b/usr/src/uts/common/sys/syscall.h	Wed Aug 21 15:45:46 2013 -0800
@@ -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 @@
 	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, ...);