Implement dt(4) utrace support on amd64 and i386.
authorclaudio <claudio@openbsd.org>
Wed, 26 Apr 2023 16:53:58 +0000 (16:53 +0000)
committerclaudio <claudio@openbsd.org>
Wed, 26 Apr 2023 16:53:58 +0000 (16:53 +0000)
This adds stacktrace_save_utrace() to extract and save the userland stack
which is stubbed out on most archs. alpha and riscv64 do not even implement
dt(4) and stacktrace_save_at() so the stubs are excluded there.

Additionally add a new ioctl DTIOCGETAUXBASE which allows btrace to
fetch the AUX_BASE vallue from the AUX vector of a process.

OK mpi@ (some time ago) discussed with kettenis@

12 files changed:
sys/arch/amd64/amd64/db_trace.c
sys/arch/arm64/arm64/db_trace.c
sys/arch/hppa/hppa/db_interface.c
sys/arch/i386/i386/db_trace.c
sys/arch/mips64/mips64/trap.c
sys/arch/powerpc/ddb/db_trace.c
sys/arch/powerpc64/powerpc64/db_trace.c
sys/arch/sparc64/sparc64/db_trace.c
sys/dev/dt/dt_dev.c
sys/dev/dt/dt_prov_profile.c
sys/dev/dt/dtvar.h
sys/sys/stacktrace.h

index e9209c3..eed87fe 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: db_trace.c,v 1.54 2021/09/04 07:13:14 jasper Exp $    */
+/*     $OpenBSD: db_trace.c,v 1.55 2023/04/26 16:53:58 claudio Exp $   */
 /*     $NetBSD: db_trace.c,v 1.1 2003/04/26 18:39:27 fvdl Exp $        */
 
 /*
@@ -287,6 +287,43 @@ stacktrace_save_at(struct stacktrace *st, unsigned int skip)
        }
 }
 
+void
+stacktrace_save_utrace(struct stacktrace *st)
+{
+       struct callframe f, *frame, *lastframe;
+       struct pcb *pcb = curpcb;
+
+       st->st_count = 0;
+
+       if (pcb == NULL)
+               return;
+
+       frame = __builtin_frame_address(0);
+       KASSERT(INKERNEL(frame));
+       f = *frame;
+
+       while (st->st_count < STACKTRACE_MAX) {
+               if (f.f_retaddr != 0 && !INKERNEL(f.f_retaddr))
+                       st->st_pc[st->st_count++] = f.f_retaddr;
+
+               lastframe = frame;
+               frame = f.f_frame;
+
+               if (frame == NULL)
+                       break;
+               if (INKERNEL(f.f_retaddr)) {
+                       if (frame <= lastframe)
+                               break;
+                       f = *frame;
+                       continue;
+               }
+               if (!INKERNEL(lastframe) && frame <= lastframe)
+                       break;
+               if (copyin(frame, &f, sizeof(f)) != 0)
+                       break;
+       }
+}
+
 vaddr_t
 db_get_pc(struct trapframe *tf)
 {
index d36db66..f0c0bd5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: db_trace.c,v 1.13 2021/07/09 20:59:51 jasper Exp $    */
+/*     $OpenBSD: db_trace.c,v 1.14 2023/04/26 16:53:58 claudio Exp $   */
 /*     $NetBSD: db_trace.c,v 1.8 2003/01/17 22:28:48 thorpej Exp $     */
 
 /*
@@ -183,3 +183,9 @@ stacktrace_save_at(struct stacktrace *st, unsigned int skip)
                        break;
        }
 }
+
+void
+stacktrace_save_utrace(struct stacktrace *st)
+{
+       st->st_count = 0;
+}
index 4fdc05d..2d278b0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: db_interface.c,v 1.48 2020/04/18 04:45:20 visa Exp $  */
+/*     $OpenBSD: db_interface.c,v 1.49 2023/04/26 16:53:58 claudio Exp $       */
 
 /*
  * Copyright (c) 1999-2003 Michael Shalayeff
@@ -341,3 +341,9 @@ stacktrace_save_at(struct stacktrace *st, unsigned int skip)
                fp = (register_t *)fp[0];
        }
 }
+
+void
+stacktrace_save_utrace(struct stacktrace *st)
+{
+       st->st_count = 0;
+}
index 8242374..5f73dab 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: db_trace.c,v 1.43 2023/01/30 10:49:05 jsg Exp $       */
+/*     $OpenBSD: db_trace.c,v 1.44 2023/04/26 16:53:58 claudio Exp $   */
 /*     $NetBSD: db_trace.c,v 1.18 1996/05/03 19:42:01 christos Exp $   */
 
 /*
@@ -292,6 +292,43 @@ stacktrace_save_at(struct stacktrace *st, unsigned int skip)
        }
 }
 
+void
+stacktrace_save_utrace(struct stacktrace *st)
+{
+       struct callframe f, *frame, *lastframe;
+       struct pcb *pcb = curpcb;
+
+       st->st_count = 0;
+
+       if (pcb == NULL)
+               return;
+
+       frame = __builtin_frame_address(0);
+       KASSERT(INKERNEL(frame));
+       f = *frame;
+
+       while (st->st_count < STACKTRACE_MAX) {
+               if (f.f_retaddr != 0 && !INKERNEL(f.f_retaddr))
+                       st->st_pc[st->st_count++] = f.f_retaddr;
+
+               lastframe = frame;
+               frame = f.f_frame;
+
+               if (frame == NULL)
+                       break;
+               if (INKERNEL(f.f_retaddr)) {
+                       if (frame <= lastframe)
+                               break;
+                       f = *frame;
+                       continue;
+               }
+               if (!INKERNEL(lastframe) && frame <= lastframe)
+                       break;
+               if (copyin(frame, &f, sizeof(f)) != 0)
+                       break;
+       }
+}
+
 vaddr_t
 db_get_pc(struct trapframe *tf)
 {
index ba018dd..df4d2ad 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.166 2023/02/11 23:07:27 deraadt Exp $      */
+/*     $OpenBSD: trap.c,v 1.167 2023/04/26 16:53:59 claudio Exp $      */
 
 /*
  * Copyright (c) 1988 University of Utah.
@@ -1479,6 +1479,12 @@ stacktrace_save_at(struct stacktrace *st, unsigned int skip)
                sp += framesize;
        }
 }
+
+void
+stacktrace_save_utrace(struct stacktrace *st)
+{
+       st->st_count = 0;
+}
 #endif
 
 #undef VALID_ADDRESS
index e9351e9..46cba9b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: db_trace.c,v 1.19 2022/02/07 22:28:15 gkoehler Exp $  */
+/*     $OpenBSD: db_trace.c,v 1.20 2023/04/26 16:53:59 claudio Exp $   */
 /*     $NetBSD: db_trace.c,v 1.15 1996/02/22 23:23:41 gwr Exp $        */
 
 /*
@@ -268,3 +268,9 @@ stacktrace_save_at(struct stacktrace *st, unsigned int skip)
                        break;
        }
 }
+
+void
+stacktrace_save_utrace(struct stacktrace *st)
+{
+       st->st_count = 0;
+}
index 9dc6322..f70d520 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: db_trace.c,v 1.8 2022/01/28 18:37:40 gkoehler Exp $   */
+/*     $OpenBSD: db_trace.c,v 1.9 2023/04/26 16:53:59 claudio Exp $    */
 /*     $NetBSD: db_trace.c,v 1.15 1996/02/22 23:23:41 gwr Exp $        */
 
 /*
@@ -232,3 +232,9 @@ stacktrace_save_at(struct stacktrace *st, unsigned int skip)
                        break;
        }
 }
+
+void
+stacktrace_save_utrace(struct stacktrace *st)
+{
+       st->st_count = 0;
+}
index 70d8f90..88e40b0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: db_trace.c,v 1.24 2022/10/25 06:00:34 guenther Exp $  */
+/*     $OpenBSD: db_trace.c,v 1.25 2023/04/26 16:53:59 claudio Exp $   */
 /*     $NetBSD: db_trace.c,v 1.23 2001/07/10 06:06:16 eeh Exp $ */
 
 /*
@@ -188,6 +188,12 @@ stacktrace_save_at(struct stacktrace *st, unsigned int skip)
        }
 }
 
+void
+stacktrace_save_utrace(struct stacktrace *st)
+{
+       st->st_count = 0;
+}
+
 void
 db_dump_window(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
 {
index 4f84e5d..96c4709 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dt_dev.c,v 1.25 2023/03/10 22:14:32 bluhm Exp $ */
+/*     $OpenBSD: dt_dev.c,v 1.26 2023/04/26 16:53:59 claudio Exp $ */
 
 /*
  * Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
 #include <sys/systm.h>
 #include <sys/param.h>
 #include <sys/device.h>
+#include <sys/exec_elf.h>
 #include <sys/malloc.h>
 #include <sys/proc.h>
+#include <sys/ptrace.h>
 
 #include <dev/dt/dtvar.h>
 
@@ -135,6 +137,7 @@ int dt_ioctl_record_start(struct dt_softc *);
 void   dt_ioctl_record_stop(struct dt_softc *);
 int    dt_ioctl_probe_enable(struct dt_softc *, struct dtioc_req *);
 int    dt_ioctl_probe_disable(struct dt_softc *, struct dtioc_req *);
+int    dt_ioctl_get_auxbase(struct dt_softc *, struct dtioc_getaux *);
 
 int    dt_pcb_ring_copy(struct dt_pcb *, struct dt_evt *, size_t, uint64_t *);
 
@@ -289,6 +292,7 @@ dtioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
        case DTIOCRECORD:
        case DTIOCPRBENABLE:
        case DTIOCPRBDISABLE:
+       case DTIOCGETAUXBASE:
                /* root only ioctl(2) */
                break;
        default:
@@ -312,6 +316,9 @@ dtioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
        case DTIOCPRBDISABLE:
                error = dt_ioctl_probe_disable(sc, (struct dtioc_req *)addr);
                break;
+       case DTIOCGETAUXBASE:
+               error = dt_ioctl_get_auxbase(sc, (struct dtioc_getaux *)addr);
+               break;
        default:
                KASSERT(0);
        }
@@ -575,6 +582,42 @@ dt_ioctl_probe_disable(struct dt_softc *sc, struct dtioc_req *dtrq)
        return 0;
 }
 
+int
+dt_ioctl_get_auxbase(struct dt_softc *sc, struct dtioc_getaux *dtga)
+{
+       struct uio uio;
+       struct iovec iov;
+       struct process *pr;
+       struct proc *p = curproc;
+       AuxInfo auxv[ELF_AUX_ENTRIES];
+       int i, error;
+
+       dtga->dtga_auxbase = 0;
+
+       if ((pr = prfind(dtga->dtga_pid)) == NULL)
+               return ESRCH;
+
+       iov.iov_base = auxv;
+       iov.iov_len = sizeof(auxv);
+       uio.uio_iov = &iov;
+       uio.uio_iovcnt = 1;
+       uio.uio_offset = pr->ps_auxinfo;
+       uio.uio_resid = sizeof(auxv);
+       uio.uio_segflg = UIO_SYSSPACE;
+       uio.uio_procp = p;
+       uio.uio_rw = UIO_READ;
+
+       error = process_domem(p, pr, &uio, PT_READ_D);
+       if (error)
+               return error;
+
+       for (i = 0; i < ELF_AUX_ENTRIES; i++)
+               if (auxv[i].au_id == AUX_base)
+                       dtga->dtga_auxbase = auxv[i].au_v;
+
+       return 0;
+}
+
 struct dt_probe *
 dt_dev_alloc_probe(const char *func, const char *name, struct dt_provider *dtpv)
 {
@@ -725,12 +768,14 @@ dt_pcb_ring_get(struct dt_pcb *dp, int profiling)
        if (ISSET(dp->dp_evtflags, DTEVT_EXECNAME))
                strlcpy(dtev->dtev_comm, p->p_p->ps_comm, sizeof(dtev->dtev_comm));
 
-       if (ISSET(dp->dp_evtflags, DTEVT_KSTACK|DTEVT_USTACK)) {
+       if (ISSET(dp->dp_evtflags, DTEVT_KSTACK)) {
                if (profiling)
                        stacktrace_save_at(&dtev->dtev_kstack, DT_FA_PROFILE);
                else
                        stacktrace_save_at(&dtev->dtev_kstack, DT_FA_STATIC);
        }
+       if (ISSET(dp->dp_evtflags, DTEVT_USTACK))
+               stacktrace_save_utrace(&dtev->dtev_ustack);
 
        return dtev;
 }
index 502e998..d6db079 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dt_prov_profile.c,v 1.4 2021/09/03 16:45:45 jasper Exp $ */
+/*     $OpenBSD: dt_prov_profile.c,v 1.5 2023/04/26 16:53:59 claudio Exp $ */
 
 /*
  * Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
@@ -27,7 +27,7 @@ struct dt_probe       *dtpp_profile;          /* per-CPU profile probe */
 struct dt_probe        *dtpp_interval;         /* global periodic probe */
 
 /* Flags that make sense for this provider */
-#define DTEVT_PROV_PROFILE     DTEVT_KSTACK
+#define DTEVT_PROV_PROFILE     DTEVT_COMMON
 
 int    dt_prov_profile_alloc(struct dt_probe *, struct dt_softc *,
            struct dt_pcb_list *, struct dtioc_req *);
index 5d4aeac..9b5d3de 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dtvar.h,v 1.16 2023/04/10 04:21:20 jsg Exp $ */
+/*     $OpenBSD: dtvar.h,v 1.17 2023/04/26 16:53:59 claudio Exp $ */
 
 /*
  * Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
@@ -53,6 +53,7 @@ struct dt_evt {
         * Recorded if the corresponding flag is set.
         */
        struct stacktrace       dtev_kstack;    /* kernel stack frame */
+       struct stacktrace       dtev_ustack;    /* userland stack frame */
        char                    dtev_comm[DTMAXCOMLEN]; /* current pr. name */
        union {
                register_t              E_entry[DTMAXFUNCARGS];
@@ -80,7 +81,6 @@ struct dt_evt {
        "\002USTACK"            \
        "\003KSTACK"            \
        "\004FUNCARGS"          \
-       "\005RETVAL"            \
 
 /*
  * Each PCB can have a filter attached to itself.  A filter do not
@@ -139,12 +139,18 @@ struct dtioc_stat {
        uint64_t                 dtst_dropevt;  /* events dropped */
 };
 
+struct dtioc_getaux {
+       pid_t                    dtga_pid;      /* process to inspect */
+       unsigned long            dtga_auxbase;  /* AUX_base value */
+};
+
 #define DTIOCGPLIST    _IOWR('D', 1, struct dtioc_probe)
 #define DTIOCGSTATS    _IOR('D', 2, struct dtioc_stat)
 #define DTIOCRECORD    _IOW('D', 3, int)
 #define DTIOCPRBENABLE _IOW('D', 4, struct dtioc_req)
 #define DTIOCPRBDISABLE         _IOW('D', 5, struct dtioc_req)
 #define DTIOCGARGS     _IOWR('D', 6, struct dtioc_arg)
+#define DTIOCGETAUXBASE         _IOWR('D', 7, struct dtioc_getaux)
 
 #ifdef _KERNEL
 
index 5d78b75..1348b71 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: stacktrace.h,v 1.3 2020/04/18 04:45:20 visa Exp $     */
+/*     $OpenBSD: stacktrace.h,v 1.4 2023/04/26 16:53:59 claudio Exp $  */
 
 /*
  * Copyright (c) 2017 Visa Hankala
@@ -29,6 +29,7 @@ struct stacktrace {
 #ifdef _KERNEL
 void   stacktrace_print(struct stacktrace *, int (*)(const char *, ...));
 void   stacktrace_save_at(struct stacktrace *, unsigned int);
+void   stacktrace_save_utrace(struct stacktrace *);
 
 static inline void
 stacktrace_save(struct stacktrace *st)