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