Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102810514
D29955.id88262.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
22 KB
Referenced Files
None
Subscribers
None
D29955.id88262.diff
View Options
diff --git a/lib/libc/sys/ptrace.2 b/lib/libc/sys/ptrace.2
--- a/lib/libc/sys/ptrace.2
+++ b/lib/libc/sys/ptrace.2
@@ -2,7 +2,7 @@
.\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $
.\"
.\" This file is in the public domain.
-.Dd July 15, 2019
+.Dd April 10, 2021
.Dt PTRACE 2
.Os
.Sh NAME
@@ -807,6 +807,49 @@
The
.Fa data
argument is ignored.
+.It Dv PT_COREDUMP
+This request creates a coredump for the stopped program.
+.Fa addr
+argument specifies a pointer to a
+.Vt "struct ptrace_coredump" ,
+which is defined as follows:
+.Bd -literal
+struct ptrace_coredump {
+ int pc_fd;
+ uint32_t pc_flags;
+ off_t pc_limit;
+};
+.Ed
+The fields of the structure are:
+.Bl -tag -width pc_flags
+.It Dv pc_fd
+File descriptor to write the dump to.
+.It Dv pc_flags
+Flags.
+The following flags are defined:
+.Bl -tag -width PC_COMPRESS
+.It Dv PC_COMPRESS
+Request compression of the dump.
+.It Dv PC_ALL
+Include non-dumpable entries into the dump.
+.El
+.It Dv pc_limit
+Maximum size of the coredump.
+Specify zero for no limit.
+.El
+.Pp
+The size of
+.Vt "struct ptrace_coredump"
+must be passed in
+.Fa data .
+.Pp
+The process must be stopped for dumping core.
+A single thread in the target process is temporarily unsuspended
+in kernel to write the dump.
+Since the thread is stopped after the write, the process must be
+waited for using
+.Xr waitpid 2
+after the request to consume the stop event.
.El
.Sh ARM MACHINE-SPECIFIC REQUESTS
.Bl -tag -width "Dv PT_SETVFPREGS"
diff --git a/sys/compat/freebsd32/freebsd32.h b/sys/compat/freebsd32/freebsd32.h
--- a/sys/compat/freebsd32/freebsd32.h
+++ b/sys/compat/freebsd32/freebsd32.h
@@ -429,4 +429,11 @@
int32_t stbcnt;
};
+struct ptrace_coredump32 {
+ int pc_fd;
+ uint32_t pc_flags;
+ uint32_t pc_limit1, pc_limit2;
+};
+
+
#endif /* !_COMPAT_FREEBSD32_FREEBSD32_H_ */
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -932,6 +932,7 @@
struct ptrace_io_desc piod;
struct ptrace_lwpinfo pl;
struct ptrace_vm_entry pve;
+ struct ptrace_coredump pc;
struct dbreg32 dbreg;
struct fpreg32 fpreg;
struct reg32 reg;
@@ -943,6 +944,7 @@
struct ptrace_io_desc32 piod;
struct ptrace_lwpinfo32 pl;
struct ptrace_vm_entry32 pve;
+ struct ptrace_coredump32 pc;
uint32_t args[nitems(td->td_sa.args)];
struct ptrace_sc_ret32 psr;
} r32;
@@ -1021,6 +1023,15 @@
CP(r32.pve, r.pve, pve_fsid);
PTRIN_CP(r32.pve, r.pve, pve_path);
break;
+ case PT_COREDUMP:
+ if (uap->data != sizeof(r32.pc))
+ error = EINVAL;
+ else
+ error = copyin(uap->addr, &r32.pc, uap->data);
+ CP(r32.pc, r.pc, pc_fd);
+ CP(r32.pc, r.pc, pc_flags);
+ r.pc.pc_limit = PAIR32TO64(off_t, r32.pc.pc_limit);
+ break;
default:
addr = uap->addr;
break;
diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c
--- a/sys/kern/imgact_elf.c
+++ b/sys/kern/imgact_elf.c
@@ -1471,12 +1471,13 @@
static void cb_size_segment(vm_map_entry_t, void *);
static int core_write(struct coredump_params *, const void *, size_t, off_t,
enum uio_seg, size_t *);
-static void each_dumpable_segment(struct thread *, segment_callback, void *);
+static void each_dumpable_segment(struct thread *, segment_callback, void *,
+ int);
static int __elfN(corehdr)(struct coredump_params *, int, void *, size_t,
- struct note_info_list *, size_t);
+ struct note_info_list *, size_t, int);
static void __elfN(prepare_notes)(struct thread *, struct note_info_list *,
size_t *);
-static void __elfN(puthdr)(struct thread *, void *, size_t, int, size_t);
+static void __elfN(puthdr)(struct thread *, void *, size_t, int, size_t, int);
static void __elfN(putnote)(struct note_info *, struct sbuf *);
static size_t register_note(struct note_info_list *, int, outfunc_t, void *);
static int sbuf_drain_core_output(void *, const char *, int);
@@ -1669,7 +1670,7 @@
/* Size the program segments. */
seginfo.count = 0;
seginfo.size = 0;
- each_dumpable_segment(td, cb_size_segment, &seginfo);
+ each_dumpable_segment(td, cb_size_segment, &seginfo, flags);
/*
* Collect info about the core file header area.
@@ -1705,7 +1706,7 @@
}
/* Create a compression stream if necessary. */
- if (compress_user_cores != 0) {
+ if (compress_user_cores != 0 && (flags & SVC_NOCOMPRESS) == 0) {
params.comp = compressor_init(core_compressed_write,
compress_user_cores, CORE_BUF_SIZE,
compress_user_cores_level, ¶ms);
@@ -1722,7 +1723,7 @@
*/
hdr = malloc(hdrsize, M_TEMP, M_WAITOK);
error = __elfN(corehdr)(¶ms, seginfo.count, hdr, hdrsize, ¬elst,
- notesz);
+ notesz, flags);
/* Write the contents of all of the writable segments. */
if (error == 0) {
@@ -1806,7 +1807,8 @@
* caller-supplied data.
*/
static void
-each_dumpable_segment(struct thread *td, segment_callback func, void *closure)
+each_dumpable_segment(struct thread *td, segment_callback func, void *closure,
+ int flags)
{
struct proc *p = td->td_proc;
vm_map_t map = &p->p_vmspace->vm_map;
@@ -1824,12 +1826,15 @@
* are marked MAP_ENTRY_NOCOREDUMP now so we no longer
* need to arbitrarily ignore such segments.
*/
- if (elf_legacy_coredump) {
- if ((entry->protection & VM_PROT_RW) != VM_PROT_RW)
- continue;
- } else {
- if ((entry->protection & VM_PROT_ALL) == 0)
- continue;
+ if ((flags & SVC_ALL) == 0) {
+ if (elf_legacy_coredump) {
+ if ((entry->protection & VM_PROT_RW) !=
+ VM_PROT_RW)
+ continue;
+ } else {
+ if ((entry->protection & VM_PROT_ALL) == 0)
+ continue;
+ }
}
/*
@@ -1838,9 +1843,11 @@
* madvise(2). Do not dump submaps (i.e. parts of the
* kernel map).
*/
- if (entry->eflags & (MAP_ENTRY_NOCOREDUMP|MAP_ENTRY_IS_SUB_MAP))
+ if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0)
+ continue;
+ if ((entry->eflags & MAP_ENTRY_NOCOREDUMP) != 0 &&
+ (flags & SVC_ALL) == 0)
continue;
-
if ((object = entry->object.vm_object) == NULL)
continue;
@@ -1867,7 +1874,8 @@
*/
static int
__elfN(corehdr)(struct coredump_params *p, int numsegs, void *hdr,
- size_t hdrsize, struct note_info_list *notelst, size_t notesz)
+ size_t hdrsize, struct note_info_list *notelst, size_t notesz,
+ int flags)
{
struct note_info *ninfo;
struct sbuf *sb;
@@ -1875,7 +1883,7 @@
/* Fill in the header. */
bzero(hdr, hdrsize);
- __elfN(puthdr)(p->td, hdr, hdrsize, numsegs, notesz);
+ __elfN(puthdr)(p->td, hdr, hdrsize, numsegs, notesz, flags);
sb = sbuf_new(NULL, NULL, CORE_BUF_SIZE, SBUF_FIXEDLEN);
sbuf_set_drain(sb, sbuf_drain_core_output, p);
@@ -1953,7 +1961,7 @@
static void
__elfN(puthdr)(struct thread *td, void *hdr, size_t hdrsize, int numsegs,
- size_t notesz)
+ size_t notesz, int flags)
{
Elf_Ehdr *ehdr;
Elf_Phdr *phdr;
@@ -2032,7 +2040,7 @@
/* All the writable segments from the program. */
phc.phdr = phdr;
phc.offset = round_page(hdrsize + notesz);
- each_dumpable_segment(td, cb_put_phdr, &phc);
+ each_dumpable_segment(td, cb_put_phdr, &phc, flags);
}
static size_t
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -2521,6 +2521,42 @@
thread_unlock(td);
}
+static void
+ptrace_coredump(struct thread *td)
+{
+ struct proc *p;
+ struct thr_coredump_req *tcq;
+ void *rl_cookie;
+
+ MPASS(td == curthread);
+ p = td->td_proc;
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ if ((td->td_dbgflags & TDB_COREDUMPRQ) == 0)
+ return;
+ KASSERT((p->p_flag & P_STOPPED_TRACE) != 0, ("not stopped"));
+
+ tcq = td->td_coredump;
+ KASSERT(tcq != NULL, ("td_coredump is NULL"));
+
+ if (p->p_sysent->sv_coredump == NULL) {
+ tcq->tc_error = ENOSYS;
+ goto wake;
+ }
+
+ PROC_UNLOCK(p);
+ rl_cookie = vn_rangelock_wlock(tcq->tc_vp, 0, OFF_MAX);
+
+ tcq->tc_error = p->p_sysent->sv_coredump(td, tcq->tc_vp,
+ tcq->tc_limit, tcq->tc_flags);
+
+ vn_rangelock_unlock(tcq->tc_vp, rl_cookie);
+ PROC_LOCK(p);
+wake:
+ td->td_dbgflags &= ~TDB_COREDUMPRQ;
+ td->td_coredump = NULL;
+ wakeup(p);
+}
+
static int
sig_suspend_threads(struct thread *td, struct proc *p, int sending)
{
@@ -2648,7 +2684,15 @@
td->td_dbgflags &= ~TDB_STOPATFORK;
}
stopme:
+ td->td_dbgflags |= TDB_SSWITCH;
thread_suspend_switch(td, p);
+ td->td_dbgflags &= ~TDB_SSWITCH;
+ if ((td->td_dbgflags & TDB_COREDUMPRQ) != 0) {
+ PROC_SUNLOCK(p);
+ ptrace_coredump(td);
+ PROC_SLOCK(p);
+ goto stopme;
+ }
if (p->p_xthread == td)
p->p_xthread = NULL;
if (!(p->p_flag & P_TRACED))
diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c
--- a/sys/kern/kern_thread.c
+++ b/sys/kern/kern_thread.c
@@ -1514,6 +1514,30 @@
return (setrunnable(td, 0));
}
+int
+thread_run_flash(struct thread *td)
+{
+ struct proc *p;
+
+ p = td->td_proc;
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+
+ if (TD_ON_SLEEPQ(td))
+ sleepq_remove_nested(td);
+ else
+ thread_lock(td);
+
+ THREAD_LOCK_ASSERT(td, MA_OWNED);
+ KASSERT(TD_IS_SUSPENDED(td), ("Thread not suspended"));
+
+ TD_CLR_SUSPENDED(td);
+ PROC_SLOCK(p);
+ MPASS(p->p_suspcount > 0);
+ p->p_suspcount--;
+ PROC_SUNLOCK(p);
+ return (setrunnable(td, 0));
+}
+
/*
* Allow all threads blocked by single threading to continue running.
*/
diff --git a/sys/kern/subr_sleepqueue.c b/sys/kern/subr_sleepqueue.c
--- a/sys/kern/subr_sleepqueue.c
+++ b/sys/kern/subr_sleepqueue.c
@@ -854,6 +854,26 @@
(void *)td, (long)td->td_proc->p_pid, td->td_name);
}
+void
+sleepq_remove_nested(struct thread *td)
+{
+ struct sleepqueue_chain *sc;
+ struct sleepqueue *sq;
+ const void *wchan;
+
+ MPASS(TD_ON_SLEEPQ(td));
+
+ wchan = td->td_wchan;
+ sc = SC_LOOKUP(wchan);
+ mtx_lock_spin(&sc->sc_lock);
+ sq = sleepq_lookup(wchan);
+ MPASS(sq != NULL);
+ thread_lock(td);
+ sleepq_remove_thread(sq, td);
+ mtx_unlock_spin(&sc->sc_lock);
+ /* Returns with the thread lock owned. */
+}
+
#ifdef INVARIANTS
/*
* UMA zone item deallocator.
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -51,6 +51,8 @@
#include <sys/sx.h>
#include <sys/malloc.h>
#include <sys/signalvar.h>
+#include <sys/caprights.h>
+#include <sys/filedesc.h>
#include <machine/reg.h>
@@ -469,6 +471,7 @@
struct ptrace_io_desc piod;
struct ptrace_lwpinfo pl;
struct ptrace_vm_entry pve;
+ struct ptrace_coredump pc;
struct dbreg dbreg;
struct fpreg fpreg;
struct reg reg;
@@ -519,6 +522,12 @@
case PT_VM_ENTRY:
error = copyin(uap->addr, &r.pve, sizeof(r.pve));
break;
+ case PT_COREDUMP:
+ if (uap->data != sizeof(r.pc))
+ error = EINVAL;
+ else
+ error = copyin(uap->addr, &r.pc, uap->data);
+ break;
default:
addr = uap->addr;
break;
@@ -601,6 +610,46 @@
p->p_ptevents = PTRACE_DEFAULT;
}
+static int
+proc_can_ptrace(struct thread *td, struct proc *p)
+{
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+
+ if ((p->p_flag & P_WEXIT) != 0)
+ return (ESRCH);
+
+ /* not being traced... */
+ if ((p->p_flag & P_TRACED) == 0)
+ return (EPERM);
+
+ /* not being traced by YOU */
+ if (p->p_pptr != td->td_proc)
+ return (EBUSY);
+
+ /* not currently stopped */
+ if ((p->p_flag & P_STOPPED_TRACE) == 0 ||
+ p->p_suspcount != p->p_numthreads ||
+ (p->p_flag & P_WAITED) == 0)
+ return (EBUSY);
+
+ return (0);
+}
+
+static struct thread *
+ptrace_sel_coredump_thread(struct proc *p)
+{
+ struct thread *td2;
+
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ MPASS((p->p_flag & P_STOPPED_TRACE) != 0);
+
+ FOREACH_THREAD_IN_PROC(p, td2) {
+ if ((td2->td_dbgflags & TDB_SSWITCH) != 0)
+ return (td2);
+ }
+ return (NULL);
+}
+
int
kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
{
@@ -611,6 +660,9 @@
struct ptrace_io_desc *piod = NULL;
struct ptrace_lwpinfo *pl;
struct ptrace_sc_ret *psr;
+ struct file *fp;
+ struct ptrace_coredump *pc;
+ struct thr_coredump_req *tcq;
int error, num, tmp;
int proctree_locked = 0;
lwpid_t tid = 0, *buf;
@@ -757,31 +809,36 @@
/* FALLTHROUGH */
default:
- /* not being traced... */
- if ((p->p_flag & P_TRACED) == 0) {
- error = EPERM;
- goto fail;
- }
-
- /* not being traced by YOU */
- if (p->p_pptr != td->td_proc) {
- error = EBUSY;
+ /*
+ * Check for ptrace eligibility before waiting for
+ * holds to drain.
+ */
+ error = proc_can_ptrace(td, p);
+ if (error != 0)
goto fail;
- }
- /* not currently stopped */
- if ((p->p_flag & P_STOPPED_TRACE) == 0 ||
- p->p_suspcount != p->p_numthreads ||
- (p->p_flag & P_WAITED) == 0) {
- error = EBUSY;
- goto fail;
+ /*
+ * Block parallel ptrace requests. Most important, do
+ * not allow other thread in debugger to continue the
+ * debuggee until coredump finished.
+ */
+ while (p->p_lock > 0) {
+ error = msleep(&p->p_lock, &p->p_mtx, PPAUSE | PCATCH,
+ "pptrace", 0);
+ if (error == 0)
+ error = proc_can_ptrace(td, p);
+ if (error != 0)
+ goto fail;
}
- /* OK */
+ /* Ok */
break;
}
- /* Keep this process around until we finish this request. */
+ /*
+ * Keep this process around and request parallel ptrace()
+ * request to wait until we finish this request.
+ */
_PHOLD(p);
/*
@@ -1299,6 +1356,65 @@
PROC_LOCK(p);
break;
+ case PT_COREDUMP:
+ pc = addr;
+ CTR2(KTR_PTRACE, "PT_COREDUMP: pid %d, fd %d",
+ p->p_pid, pc->pc_fd);
+
+ if ((pc->pc_flags & ~(PC_COMPRESS | PC_ALL)) != 0) {
+ error = EINVAL;
+ break;
+ }
+ PROC_UNLOCK(p);
+
+ tcq = malloc(sizeof(*tcq), M_TEMP, M_WAITOK | M_ZERO);
+ fp = NULL;
+ error = fget_write(td, pc->pc_fd, &cap_write_rights, &fp);
+ if (error != 0)
+ goto coredump_cleanup_nofp;
+ if (fp->f_type != DTYPE_VNODE || fp->f_vnode->v_type != VREG) {
+ error = EPIPE;
+ goto coredump_cleanup;
+ }
+
+ PROC_LOCK(p);
+ error = proc_can_ptrace(td, p);
+ if (error != 0)
+ goto coredump_cleanup_locked;
+
+ td2 = ptrace_sel_coredump_thread(p);
+ if (td2 == NULL) {
+ error = EBUSY;
+ goto coredump_cleanup_locked;
+ }
+ KASSERT((td2->td_dbgflags & TDB_COREDUMPRQ) == 0,
+ ("proc %d tid %d req coredump", p->p_pid, td2->td_tid));
+
+ tcq->tc_vp = fp->f_vnode;
+ tcq->tc_limit = pc->pc_limit == 0 ? OFF_MAX : pc->pc_limit;
+ if ((pc->pc_flags & PC_COMPRESS) == 0)
+ tcq->tc_flags |= SVC_NOCOMPRESS;
+ if ((pc->pc_flags & PC_ALL) != 0)
+ tcq->tc_flags |= SVC_ALL;
+ td2->td_coredump = tcq;
+ td2->td_dbgflags |= TDB_COREDUMPRQ;
+ if (thread_run_flash(td2)) {
+ PROC_UNLOCK(p);
+ kick_proc0();
+ PROC_LOCK(p);
+ }
+ while ((td2->td_dbgflags & TDB_COREDUMPRQ) != 0)
+ msleep(p, &p->p_mtx, PPAUSE, "crdmp", 0);
+ error = tcq->tc_error;
+coredump_cleanup_locked:
+ PROC_UNLOCK(p);
+coredump_cleanup:
+ fdrop(fp, td);
+coredump_cleanup_nofp:
+ free(tcq, M_TEMP);
+ PROC_LOCK(p);
+ break;
+
default:
#ifdef __HAVE_PTRACE_MACHDEP
if (req >= PT_FIRSTMACH) {
@@ -1311,7 +1427,6 @@
error = EINVAL;
break;
}
-
out:
/* Drop our hold on this process now that the request has completed. */
_PRELE(p);
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -377,6 +377,7 @@
int td_oncpu; /* (t) Which cpu we are on. */
void *td_lkpi_task; /* LinuxKPI task struct pointer */
int td_pmcpend;
+ void *td_coredump; /* (c) coredump request. */
#ifdef EPOCH_TRACE
SLIST_HEAD(, epoch_tracker) td_epochs;
#endif
@@ -485,6 +486,8 @@
#define TDB_VFORK 0x00000800 /* vfork indicator for ptrace() */
#define TDB_FSTP 0x00001000 /* The thread is PT_ATTACH leader */
#define TDB_STEP 0x00002000 /* (x86) PSL_T set for PT_STEP */
+#define TDB_SSWITCH 0x00004000 /* Suspended in ptracestop */
+#define TDB_COREDUMPRQ 0x00008000 /* Coredump request */
/*
* "Private" flags kept in td_pflags:
@@ -1191,6 +1194,7 @@
void childproc_stopped(struct proc *child, int reason);
void childproc_continued(struct proc *child);
void childproc_exited(struct proc *child);
+int thread_run_flash(struct thread *td);
int thread_suspend_check(int how);
bool thread_suspend_check_needed(void);
void thread_suspend_switch(struct thread *, struct proc *p);
diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h
--- a/sys/sys/ptrace.h
+++ b/sys/sys/ptrace.h
@@ -74,6 +74,8 @@
#define PT_GET_SC_ARGS 27 /* fetch syscall args */
#define PT_GET_SC_RET 28 /* fetch syscall results */
+#define PT_COREDUMP 29 /* create a coredump */
+
#define PT_GETREGS 33 /* get general-purpose registers */
#define PT_SETREGS 34 /* set general-purpose registers */
#define PT_GETFPREGS 35 /* get floating-point registers */
@@ -176,8 +178,27 @@
char *pve_path; /* Path name of object. */
};
+/* Argument structure for PT_COREDUMP */
+struct ptrace_coredump {
+ int pc_fd; /* File descriptor to write dump to. */
+ uint32_t pc_flags; /* Flags PC_* */
+ off_t pc_limit; /* Maximum size of the coredump,
+ 0 for no limit. */
+};
+
+/* Flags for PT_COREDUMP pc_flags */
+#define PC_COMPRESS 0x00000001 /* Allow compression */
+#define PC_ALL 0x00000002 /* Include non-dumpable entries */
+
#ifdef _KERNEL
+struct thr_coredump_req {
+ struct vnode *tc_vp; /* vnode to write coredump to. */
+ off_t tc_limit; /* max coredump file size. */
+ int tc_flags; /* user flags */
+ int tc_error; /* request result */
+};
+
int ptrace_set_pc(struct thread *_td, unsigned long _addr);
int ptrace_single_step(struct thread *_td);
int ptrace_clear_single_step(struct thread *_td);
diff --git a/sys/sys/sleepqueue.h b/sys/sys/sleepqueue.h
--- a/sys/sys/sleepqueue.h
+++ b/sys/sys/sleepqueue.h
@@ -100,6 +100,7 @@
void sleepq_remove(struct thread *td, const void *wchan);
int sleepq_remove_matching(struct sleepqueue *sq, int queue,
bool (*matches)(struct thread *), int pri);
+void sleepq_remove_nested(struct thread *td);
int sleepq_signal(const void *wchan, int flags, int pri, int queue);
void sleepq_set_timeout_sbt(const void *wchan, sbintime_t sbt,
sbintime_t pr, int flags);
diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h
--- a/sys/sys/sysent.h
+++ b/sys/sys/sysent.h
@@ -171,6 +171,10 @@
#define SV_ABI_CLOUDABI 17
#define SV_ABI_UNDEF 255
+/* sv_coredump flags */
+#define SVC_NOCOMPRESS 0x00000001 /* disable compression. */
+#define SVC_ALL 0x00000002 /* dump everything */
+
#ifdef _KERNEL
extern struct sysentvec aout_sysvec;
extern struct sysent sysent[];
diff --git a/usr.bin/gcore/gcore.1 b/usr.bin/gcore/gcore.1
--- a/usr.bin/gcore/gcore.1
+++ b/usr.bin/gcore/gcore.1
@@ -28,7 +28,7 @@
.\" @(#)gcore.1 8.2 (Berkeley) 4/18/94
.\" $FreeBSD$
.\"
-.Dd July 13, 2016
+.Dd April 24, 2021
.Dt GCORE 1
.Os
.Sh NAME
@@ -36,8 +36,9 @@
.Nd get core images of running process
.Sh SYNOPSIS
.Nm
-.Op Fl f
.Op Fl c Ar core
+.Op Fl f
+.Op Fl k
.Op Ar executable
.Ar pid
.Sh DESCRIPTION
@@ -65,6 +66,16 @@
As a result, this flag should only be used when the behavior of the
application and any devices it has mapped is fully understood and any side
effects can be controlled or tolerated.
+.It Fl k
+Use kernel facility
+.Xr ptrace 2
+.Dv PT_COREDUMP
+to write the core dump, instead of reading the process memory and constructing
+the dump by
+.Nm
+code.
+This is both faster and more precise, also the dump is written by the
+same kernel code that writes core dumps upon fatal signals.
.El
.Sh FILES
.Bl -tag -width /var/log/messages -compact
diff --git a/usr.bin/gcore/gcore.c b/usr.bin/gcore/gcore.c
--- a/usr.bin/gcore/gcore.c
+++ b/usr.bin/gcore/gcore.c
@@ -56,13 +56,16 @@
*/
#include <sys/param.h>
+#include <sys/ptrace.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/linker_set.h>
#include <sys/sysctl.h>
+#include <sys/wait.h>
#include <err.h>
#include <fcntl.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -75,21 +78,77 @@
static void usage(void) __dead2;
static pid_t pid;
+static bool kflag = false;
SET_DECLARE(dumpset, struct dumpers);
+static int
+open_corefile(char *corefile)
+{
+ char fname[MAXPATHLEN];
+ int fd;
+
+ if (corefile == NULL) {
+ (void)snprintf(fname, sizeof(fname), "core.%d", pid);
+ corefile = fname;
+ }
+ fd = open(corefile, O_RDWR | O_CREAT | O_TRUNC, DEFFILEMODE);
+ if (fd < 0)
+ err(1, "%s", corefile);
+ return (fd);
+}
+
+static void
+kcoredump(int fd, pid_t pid)
+{
+ struct ptrace_coredump pc;
+ int error, res, ret, waited;
+
+ error = ptrace(PT_ATTACH, pid, NULL, 0);
+ if (error != 0)
+ err(1, "attach");
+
+ waited = waitpid(pid, &res, 0);
+ if (waited == -1)
+ err(1, "wait for STOP");
+
+ ret = 0;
+ memset(&pc, 0, sizeof(pc));
+ pc.pc_fd = fd;
+ pc.pc_flags = (pflags & PFLAGS_FULL) != 0 ? PC_ALL : 0;
+ error = ptrace(PT_COREDUMP, pid, (void *)&pc, sizeof(pc));
+ if (error == -1) {
+ warn("coredump");
+ ret = 1;
+ }
+
+ waited = waitpid(pid, &res, 0);
+ if (waited == -1) {
+ warn("wait after coredump");
+ ret = 1;
+ }
+
+ error = ptrace(PT_DETACH, pid, NULL, 0);
+ if (error == -1) {
+ warn("detach failed, check process status");
+ ret = 1;
+ }
+
+ exit(ret);
+}
+
int
main(int argc, char *argv[])
{
int ch, efd, fd, name[4];
char *binfile, *corefile;
- char passpath[MAXPATHLEN], fname[MAXPATHLEN];
+ char passpath[MAXPATHLEN];
struct dumpers **d, *dumper;
size_t len;
pflags = 0;
corefile = NULL;
- while ((ch = getopt(argc, argv, "c:f")) != -1) {
+ while ((ch = getopt(argc, argv, "c:fk")) != -1) {
switch (ch) {
case 'c':
corefile = optarg;
@@ -97,6 +156,9 @@
case 'f':
pflags |= PFLAGS_FULL;
break;
+ case 'k':
+ kflag = true;
+ break;
default:
usage();
break;
@@ -104,10 +166,26 @@
}
argv += optind;
argc -= optind;
+
/* XXX we should check that the pid argument is really a number */
switch (argc) {
case 1:
pid = atoi(argv[0]);
+ break;
+ case 2:
+ binfile = argv[0];
+ pid = atoi(argv[1]);
+ break;
+ default:
+ usage();
+ }
+
+ if (kflag) {
+ fd = open_corefile(corefile);
+ kcoredump(fd, pid);
+ }
+
+ if (argc == 1) {
name[0] = CTL_KERN;
name[1] = KERN_PROC;
name[2] = KERN_PROC_PATHNAME;
@@ -116,13 +194,6 @@
if (sysctl(name, 4, passpath, &len, NULL, 0) == -1)
errx(1, "kern.proc.pathname failure");
binfile = passpath;
- break;
- case 2:
- pid = atoi(argv[1]);
- binfile = argv[0];
- break;
- default:
- usage();
}
efd = open(binfile, O_RDONLY, 0);
if (efd < 0)
@@ -138,13 +209,7 @@
}
if (dumper == NULL)
errx(1, "Invalid executable file");
- if (corefile == NULL) {
- (void)snprintf(fname, sizeof(fname), "core.%d", pid);
- corefile = fname;
- }
- fd = open(corefile, O_RDWR|O_CREAT|O_TRUNC, DEFFILEMODE);
- if (fd < 0)
- err(1, "%s", corefile);
+ fd = open_corefile(corefile);
dumper->dump(efd, fd, pid);
(void)close(fd);
@@ -156,6 +221,7 @@
usage(void)
{
- (void)fprintf(stderr, "usage: gcore [-c core] [executable] pid\n");
+ (void)fprintf(stderr,
+ "usage: gcore [-kf] [-c core] [executable] pid\n");
exit(1);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Nov 18, 12:01 PM (12 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14697038
Default Alt Text
D29955.id88262.diff (22 KB)
Attached To
Mode
D29955: ptrace(PT_COREDUMP)
Attached
Detach File
Event Timeline
Log In to Comment