/* * 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. ISA-dependent functions are implemented * in gcore_isadep.c. */ #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef exit #include #include #include #include #include #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 /* 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 != 0) { svd = mdb_alloc(sizeof (*svd), UM_SLEEP); if (mdb_ctf_vread(svd, "segvn_data_t", "mdb_segvn_data_t", seg->s_data, 0) == -1) { goto error; } if (svd->pageprot != 0) { nvpage = seg_pages(seg); dprintf("vpage count: %d\n", nvpage); vpage = mdb_alloc(vpgtob(nvpage), UM_SLEEP); if (mdb_vread(vpage, vpgtob(nvpage), (uintptr_t)svd->vpage) != vpgtob(nvpage)) { mdb_warn("Failed to read vpages from %p\n", svd->vpage); goto error; } svd->vpage = vpage; } else { svd->vpage = NULL; } gs->gs_data = svd; } else { gs->gs_data = NULL; } return (0); error: mdb_free(vpage, vpgtob(nvpage)); mdb_free(svd, sizeof (*svd)); return (-1); } /*ARGSUSED*/ static int gsvn_getoffset(gcore_seg_t *gs, u_offset_t addr) { mdb_segvn_data_t *svd = gs->gs_data; mdb_seg_t *seg = gs->gs_seg; return (svd->offset + (uintptr_t)(addr - seg->s_base)); } static void gsvn_name(gcore_seg_t *gs, char *name, size_t size) { mdb_segvn_data_t *svd = gs->gs_data; name[0] = '\0'; if (svd->vp != 0) { mdb_seg_t *seg = gs->gs_seg; mdb_as_t as; mdb_proc_t p; mdb_vnode_t vn; if (mdb_ctf_vread(&vn, "vnode_t", "mdb_vnode_t", svd->vp, 0) == -1) { return; } if (mdb_ctf_vread(&as, "struct as", "mdb_as_t", seg->s_as, 0) == -1) { return; } if (mdb_ctf_vread(&p, "proc_t", "mdb_proc_t", as.a_proc, 0) == -1) { return; } if (vn.v_type == VREG && svd->vp == p.p_exec) { (void) strncpy(name, "a.out", size); } /* * procfs has more logic here to construct a name using * vfs/vnode identifiers but didn't seem worthwhile to add * here. */ } } /*ARGSUSED*/ static int gsvn_gettype(gcore_seg_t *gs, u_offset_t addr) { return (0); } static void gsvn_fini(gcore_seg_t *gs) { mdb_segvn_data_t *svd = gs->gs_data; if (svd != NULL) { if (svd->vpage != NULL) { size_t nvpage = seg_pages(gs->gs_seg); mdb_free(svd->vpage, vpgtob(nvpage)); } mdb_free(svd, sizeof (*svd)); } } static boolean_t gsvn_noreserve(gcore_seg_t *gs) { mdb_segvn_data_t *svd = gs->gs_data; if (svd == NULL) { return (B_FALSE); } if (svd->flags & MAP_NORESERVE) { mdb_vnode_t vn; if (svd->vp == 0) { return (B_TRUE); } if (mdb_ctf_vread(&vn, "vnode_t", "mdb_vnode_t", svd->vp, 0) == -1) { return (B_FALSE); } if (vn.v_type != VREG) { return (B_TRUE); } } return (B_FALSE); } static uintptr_t gcore_anon_get_ptr(uintptr_t ah_addr, ulong_t an_idx) { mdb_anon_hdr_t ah; uintptr_t anon_addr; uintptr_t anon_ptr; if (mdb_ctf_vread(&ah, "struct anon_hdr", "mdb_anon_hdr_t", ah_addr, 0) == -1) { return (0); } /* * Single level case. */ if ((ah.size <= ANON_CHUNK_SIZE) || (ah.flags & ANON_ALLOC_FORCE)) { anon_addr = ah.array_chunk + (sizeof (anon_ptr) * an_idx); if (mdb_vread(&anon_ptr, sizeof (anon_ptr), anon_addr) != sizeof (anon_ptr)) { mdb_warn("Failed to read anon_ptr from %p (1 level)\n", anon_addr); return (0); } return (anon_ptr & ANON_PTRMASK); } /* * 2 level case. */ anon_addr = ah.array_chunk + (sizeof (anon_ptr) * (an_idx >> ANON_CHUNK_SHIFT)); if (mdb_vread(&anon_ptr, sizeof (anon_ptr), anon_addr) != sizeof (anon_ptr)) { mdb_warn("Failed to read anon_ptr from %p (2a level)\n", anon_addr); return (0); } if (anon_ptr == 0) { return (0); } anon_addr = anon_ptr + (sizeof (anon_ptr) * (an_idx & ANON_CHUNK_OFF)); if (mdb_vread(&anon_ptr, sizeof (anon_ptr), anon_addr) != sizeof (anon_ptr)) { mdb_warn("Failed to read anon_ptr from %p (2b level)\n", anon_addr); return (0); } return (anon_ptr & ANON_PTRMASK); } static void gcore_anon_get(uintptr_t ahp, ulong_t an_index, uintptr_t *vp, u_offset_t *off) { mdb_anon_t anon; uintptr_t ap; ap = gcore_anon_get_ptr(ahp, an_index); if (ap != 0) { if (mdb_ctf_vread(&anon, "struct anon", "mdb_anon_t", ap, 0) == -1) { return; } *vp = anon.an_vp; *off = anon.an_off; } else { *vp = 0; *off = 0; } } static u_offset_t gsvn_incore(gcore_seg_t *gs, u_offset_t addr, u_offset_t eaddr) { mdb_segvn_data_t *svd = gs->gs_data; mdb_seg_t *seg = gs->gs_seg; mdb_amp_t amp; u_offset_t offset; uintptr_t vp; size_t p, ep; if (svd->amp != 0 && mdb_ctf_vread(&, "amp_t", "mdb_amp_t", svd->amp, 0) == -1) { return (eaddr); } p = seg_page(seg, addr); ep = seg_page(seg, eaddr); for (; p < ep; p++, addr += PAGESIZE) { /* First check the anon map */ if (svd->amp != 0) { gcore_anon_get(amp.ahp, svd->anon_index + p, &vp, &offset); if (vp != 0 && mdb_page_lookup(vp, offset) != 0) { break; } } /* Now check the segment's vnode */ vp = svd->vp; offset = svd->offset + (addr - gs->gs_seg->s_base); if (mdb_page_lookup(vp, offset) != 0) { break; } dprintf("amp: %p vp: %p addr: %p offset: %p not in core!\n", svd->amp, svd->vp, addr, offset); } return (addr); } static uint_t gsvn_getprot(gcore_seg_t *gs, u_offset_t addr) { mdb_segvn_data_t *svd = gs->gs_data; mdb_seg_t *seg = gs->gs_seg; if (svd->pageprot == 0) { return (svd->prot); } dprintf("addr: %p pgno: %p\n", addr, seg_page(seg, addr)); return (VPP_PROT(&svd->vpage[seg_page(seg, addr)])); } /* * Helper functions for constructing the process address space maps. */ /*ARGSUSED*/ static int as_segat_cb(uintptr_t seg_addr, const void *aw_buff, void *arg) { as_segat_cbarg_t *as_segat_arg = arg; mdb_seg_t seg; if (mdb_ctf_vread(&seg, "struct seg", "mdb_seg_t", seg_addr, 0) == -1) { return (WALK_ERR); } if (as_segat_arg->addr < seg.s_base) { return (WALK_NEXT); } if (as_segat_arg->addr >= seg.s_base + seg.s_size) { return (WALK_NEXT); } as_segat_arg->res = seg_addr; return (WALK_DONE); } /* * Find a segment containing addr. */ static uintptr_t gcore_as_segat(uintptr_t as_addr, uintptr_t addr) { as_segat_cbarg_t as_segat_arg; uintptr_t segtree_addr; as_segat_arg.addr = addr; as_segat_arg.res = 0; segtree_addr = as_addr + mdb_ctf_offsetof_by_name("struct as", "a_segtree"); (void) avl_walk_mdb(segtree_addr, as_segat_cb, &as_segat_arg); return (as_segat_arg.res); } static uintptr_t gcore_break_seg(mdb_proc_t *p) { uintptr_t addr = p->p_brkbase; if (p->p_brkbase != 0) addr += p->p_brksize - 1; return (gcore_as_segat(p->p_as, addr)); } 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 == 0) { 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 = 0; uintptr_t t_onproc = 0; /* running on processor */ uintptr_t t_run = 0; /* runnable, on disp queue */ uintptr_t t_sleep = 0; /* sleeping */ uintptr_t t_susp = 0; /* suspended stop */ uintptr_t t_jstop = 0; /* jobcontrol stop, w/o directed stop */ uintptr_t t_jdstop = 0; /* jobcontrol stop with directed stop */ uintptr_t t_req = 0; /* requested stop */ uintptr_t t_istop = 0; /* event-of-interest stop */ uintptr_t t_dtrace = 0; /* DTrace stop */ /* * If the agent lwp exists, it takes precedence over all others. */ if ((t_addr = p->p_agenttp) != 0) { return (t_addr); } if ((t_addr = p->p_tlist) == 0) /* 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 == 0) t_req = t_addr; continue; } switch (t->t_state) { default: return (0); case TS_SLEEP: if (t_sleep == 0) t_sleep = t_addr; break; case TS_RUN: case TS_WAIT: if (t_run == 0) t_run = t_addr; break; case TS_ONPROC: if (t_onproc == 0) 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 == 0) t_susp = t_addr; break; case PR_JOBCONTROL: if (t->t_proc_flag & TP_PRSTOP) { if (t_jdstop == 0) t_jdstop = t_addr; } else { if (t_jstop == 0) t_jstop = t_addr; } break; case PR_REQUESTED: if (t->t_dtrace_stop && t_dtrace == 0) t_dtrace = t_addr; else if (t_req == 0) 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 == 0 || (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 == 0) { 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; } } } 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; sp->pr_sysarg[3] = 0; 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 == 0) { 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 != 0) { 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)) == 0) { 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 == 0) { 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; 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 (Pgcore(P, core_name, CC_CONTENT_DEFAULT) != 0) { mdb_warn("Failed to generate core file: %d", errno); 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 */