commit ea5f74cd28f31b9dbce1e41dabebacecc2d416e0 Author: Mark Johnston Date: Fri Jul 24 21:25:20 2015 -0700 stack(9): allow stack capture of a running thread on amd64 diff --git a/sys/amd64/amd64/stack_machdep.c b/sys/amd64/amd64/stack_machdep.c index 57908e2..6dd5e4f 100644 --- a/sys/amd64/amd64/stack_machdep.c +++ b/sys/amd64/amd64/stack_machdep.c @@ -29,16 +29,26 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include +#include #include #include #include +#include #include #include #include #include +static struct stack *nmi_stack; +static volatile struct thread *nmi_pending; + +static struct mtx nmi_lock; +MTX_SYSINIT(nmi_lock, &nmi_lock, "stack_nmi", MTX_SPIN); + static void stack_capture(struct stack *st, register_t rbp) { @@ -63,6 +73,19 @@ stack_capture(struct stack *st, register_t rbp) } } +int +stack_nmi_handler(struct trapframe *tf) +{ + + if (nmi_stack == NULL) + return (0); + + MPASS(curthread == nmi_pending); + stack_capture(nmi_stack, tf->tf_rbp); + nmi_pending = NULL; + return (1); +} + void stack_save_td(struct stack *st, struct thread *td) { @@ -77,6 +100,39 @@ stack_save_td(struct stack *st, struct thread *td) stack_capture(st, rbp); } +int +stack_save_td_running(struct stack *st, struct thread *td) +{ + + THREAD_LOCK_ASSERT(td, MA_OWNED); + MPASS(TD_IS_RUNNING(td)); + + if (td == curthread) { + stack_save(st); + return (0); + } + + if (p_candebug(curthread, td->td_proc) != 0) + return (1); + + mtx_lock_spin(&nmi_lock); + + nmi_stack = st; + nmi_pending = td; + ipi_cpu(td->td_oncpu, IPI_TRACE); + while (nmi_pending != NULL) + cpu_spinwait(); + nmi_stack = NULL; + + mtx_unlock_spin(&nmi_lock); + + if (st->depth == 0) + /* We interrupted a thread in user mode. */ + return (1); + + return (0); +} + void stack_save(struct stack *st) { diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c index fa74eb2..1170cbf 100644 --- a/sys/amd64/amd64/trap.c +++ b/sys/amd64/amd64/trap.c @@ -91,6 +91,7 @@ PMC_SOFT_DEFINE( , , page_fault, write); #ifdef SMP #include #endif +#include #include #ifdef KDTRACE_HOOKS @@ -202,17 +203,20 @@ trap(struct trapframe *frame) goto out; } -#ifdef HWPMC_HOOKS - /* - * CPU PMCs interrupt using an NMI. If the PMC module is - * active, pass the 'rip' value to the PMC module's interrupt - * handler. A return value of '1' from the handler means that - * the NMI was handled by it and we can return immediately. - */ - if (type == T_NMI && pmc_intr && - (*pmc_intr)(PCPU_GET(cpuid), frame)) - goto out; + if (type == T_NMI) { + if (stack_nmi_handler(frame) != 0) + goto out; +#ifdef HWPMC_HOOKS + /* + * CPU PMCs interrupt using an NMI. If the PMC module is + * active, pass the 'rip' value to the PMC module's interrupt + * handler. A non-zero return value from the handler means that + * the NMI was consumed by it and we can return immediately. + */ + if (pmc_intr != NULL && (*pmc_intr)(PCPU_GET(cpuid), frame)) + goto out; #endif + } if (type == T_MCHK) { mca_intr(); diff --git a/sys/amd64/include/stack.h b/sys/amd64/include/stack.h index 8297eae..934f476f 100644 --- a/sys/amd64/include/stack.h +++ b/sys/amd64/include/stack.h @@ -39,4 +39,6 @@ struct amd64_frame { long f_arg0; }; +int stack_nmi_handler(struct trapframe *); + #endif /* !_MACHINE_STACK_H_ */ diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 27c6f40..51a6204 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -2516,11 +2516,14 @@ repeat: sizeof(kkstp->kkst_trace), SBUF_FIXEDLEN); thread_lock(td); kkstp->kkst_tid = td->td_tid; - if (TD_IS_SWAPPED(td)) + if (TD_IS_SWAPPED(td)) { kkstp->kkst_state = KKST_STATE_SWAPPED; - else if (TD_IS_RUNNING(td)) - kkstp->kkst_state = KKST_STATE_RUNNING; - else { + } else if (TD_IS_RUNNING(td)) { + if (stack_save_td_running(st, td) == 0) + kkstp->kkst_state = KKST_STATE_STACKOK; + else + kkstp->kkst_state = KKST_STATE_RUNNING; + } else { kkstp->kkst_state = KKST_STATE_STACKOK; stack_save_td(st, td); } diff --git a/sys/sys/stack.h b/sys/sys/stack.h index 5531467..4944514 100644 --- a/sys/sys/stack.h +++ b/sys/sys/stack.h @@ -60,5 +60,6 @@ void stack_ktr(u_int, const char *, int, const struct stack *, struct thread; void stack_save(struct stack *); void stack_save_td(struct stack *, struct thread *); +int stack_save_td_running(struct stack *, struct thread *); #endif diff --git a/sys/x86/include/apicvar.h b/sys/x86/include/apicvar.h index 0bd9fe5..8d9c475 100644 --- a/sys/x86/include/apicvar.h +++ b/sys/x86/include/apicvar.h @@ -129,12 +129,14 @@ #else #define IPI_DYN_FIRST (APIC_IPI_INTS + 8) #endif -#define IPI_DYN_LAST (254) /* IPIs allocated at runtime */ +#define IPI_DYN_LAST (253) /* IPIs allocated at runtime */ /* - * IPI_STOP_HARD does not need to occupy a slot in the IPI vector space since - * it is delivered using an NMI anyways. + * IPI_TRACE and IPI_STOP_HARD do not need to occupy a slot in the IPI vector + * space since they are delivered using an NMI. */ +#define IPI_NMI_FIRST 254 +#define IPI_TRACE 254 /* Interrupt for tracing. */ #define IPI_STOP_HARD 255 /* Stop CPU with a NMI. */ /* diff --git a/sys/x86/x86/local_apic.c b/sys/x86/x86/local_apic.c index d75a452..96722da 100644 --- a/sys/x86/x86/local_apic.c +++ b/sys/x86/x86/local_apic.c @@ -1697,11 +1697,10 @@ native_lapic_ipi_vectored(u_int vector, int dest) icrlo = APIC_DESTMODE_PHY | APIC_TRIGMOD_EDGE | APIC_LEVEL_ASSERT; /* - * IPI_STOP_HARD is just a "fake" vector used to send a NMI. - * Use special rules regard NMI if passed, otherwise specify - * the vector. + * NMI IPIs are just fake vectors used to send a NMI. Use special rules + * regarding NMIs if passed, otherwise specify the vector. */ - if (vector == IPI_STOP_HARD) + if (vector >= IPI_NMI_FIRST) icrlo |= APIC_DELMODE_NMI; else icrlo |= vector | APIC_DELMODE_FIXED; diff --git a/sys/x86/x86/mp_x86.c b/sys/x86/x86/mp_x86.c index b4c66a5..08394d4 100644 --- a/sys/x86/x86/mp_x86.c +++ b/sys/x86/x86/mp_x86.c @@ -120,7 +120,7 @@ struct cpu_ops cpu_ops; * Local data and functions. */ -static volatile cpuset_t ipi_nmi_pending; +static volatile cpuset_t ipi_stop_nmi_pending; /* used to hold the AP's until we are ready to release them */ struct mtx ap_boot_mtx; @@ -894,7 +894,7 @@ ipi_selected(cpuset_t cpus, u_int ipi) * Set the mask of receiving CPUs for this purpose. */ if (ipi == IPI_STOP_HARD) - CPU_OR_ATOMIC(&ipi_nmi_pending, &cpus); + CPU_OR_ATOMIC(&ipi_stop_nmi_pending, &cpus); while ((cpu = CPU_FFS(&cpus)) != 0) { cpu--; @@ -917,7 +917,7 @@ ipi_cpu(int cpu, u_int ipi) * Set the mask of receiving CPUs for this purpose. */ if (ipi == IPI_STOP_HARD) - CPU_SET_ATOMIC(cpu, &ipi_nmi_pending); + CPU_SET_ATOMIC(cpu, &ipi_stop_nmi_pending); CTR3(KTR_SMP, "%s: cpu: %d ipi: %x", __func__, cpu, ipi); ipi_send_cpu(cpu, ipi); @@ -944,14 +944,14 @@ ipi_all_but_self(u_int ipi) * Set the mask of receiving CPUs for this purpose. */ if (ipi == IPI_STOP_HARD) - CPU_OR_ATOMIC(&ipi_nmi_pending, &other_cpus); + CPU_OR_ATOMIC(&ipi_stop_nmi_pending, &other_cpus); CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi); lapic_ipi_vectored(ipi, APIC_IPI_DEST_OTHERS); } int -ipi_nmi_handler() +ipi_nmi_handler(void) { u_int cpuid; @@ -962,10 +962,10 @@ ipi_nmi_handler() * and should be handled. */ cpuid = PCPU_GET(cpuid); - if (!CPU_ISSET(cpuid, &ipi_nmi_pending)) + if (!CPU_ISSET(cpuid, &ipi_stop_nmi_pending)) return (1); - CPU_CLR_ATOMIC(cpuid, &ipi_nmi_pending); + CPU_CLR_ATOMIC(cpuid, &ipi_stop_nmi_pending); cpustop_handler(); return (0); }