On 07.07.21 22:16, Jan Kiszka via Xenomai wrote:
> From: Jan Kiszka <[email protected]>
>
> This introduces the concept of a debugging helper thread. It can
> register itself with the cobalt core and then wait for ptrace stop and
> resumption events. This can be used to bring the connected devices or
> other parts of the system into a state that can tolerate the potentially
> long interruption and also synchronize again with it to continue.
>
> On stop events (breakpoints, debugger interceptions), the core will
> ensure that the helper is run after all primary-mode threads were put on
> hold and before the debugger will gain control over the whole process.
> For that purpose, the helper will receive a notification when it was
> pending on the corresponding event-wait syscall and will release the
> process into debugging by invoking that syscall to wait for the
> resumption event.
>
> When the debugger resumes the whole process, the helper is resumed
> first, right before all threads that will continue in primary mode are
> released (threads in secondary mode may run earlier but will have to
> wait when trying to enter primary mode). Again, the helper thread
> decides when to continue by calling the event-wait system again, in
> that case in order to wait for the next stop event.
>
> Signed-off-by: Jan Kiszka <[email protected]>
> ---
> include/cobalt/Makefile.am | 1 +
> include/cobalt/ptrace.h | 37 ++++++++++++++
> include/cobalt/uapi/Makefile.am | 1 +
> include/cobalt/uapi/ptrace.h | 24 +++++++++
> include/cobalt/uapi/syscall.h | 2 +
> kernel/cobalt/posix/process.c | 34 +++++++++++-
> kernel/cobalt/posix/process.h | 5 ++
> kernel/cobalt/posix/syscall.c | 86 +++++++++++++++++++++++++++++++
> lib/cobalt/Makefile.am | 1 +
> lib/cobalt/ptrace.c | 91 +++++++++++++++++++++++++++++++++
> 10 files changed, 280 insertions(+), 2 deletions(-)
> create mode 100644 include/cobalt/ptrace.h
> create mode 100644 include/cobalt/uapi/ptrace.h
> create mode 100644 lib/cobalt/ptrace.c
>
> diff --git a/include/cobalt/Makefile.am b/include/cobalt/Makefile.am
> index 19e96112e8..e0b203193d 100644
> --- a/include/cobalt/Makefile.am
> +++ b/include/cobalt/Makefile.am
> @@ -4,6 +4,7 @@ includesub_HEADERS = \
> fcntl.h \
> mqueue.h \
> pthread.h \
> + ptrace.h \
> sched.h \
> semaphore.h \
> signal.h \
> diff --git a/include/cobalt/ptrace.h b/include/cobalt/ptrace.h
> new file mode 100644
> index 0000000000..f5bec56c9d
> --- /dev/null
> +++ b/include/cobalt/ptrace.h
> @@ -0,0 +1,37 @@
> +/*
> + * Copyright (C) Siemens AG, 2015-2021
> + *
> + * Authors:
> + * Jan Kiszka <[email protected]>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
> + */
> +#ifndef _COBALT_PTRACE_H
> +#define _COBALT_PTRACE_H
> +
> +#include <cobalt/uapi/ptrace.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +int cobalt_ptrace_helper_init(void);
> +int cobalt_ptrace_event_wait(int event);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* !_COBALT_PTRACE_H */
> diff --git a/include/cobalt/uapi/Makefile.am b/include/cobalt/uapi/Makefile.am
> index d887213f8e..41076e23d9 100644
> --- a/include/cobalt/uapi/Makefile.am
> +++ b/include/cobalt/uapi/Makefile.am
> @@ -6,6 +6,7 @@ includesub_HEADERS = \
> event.h \
> monitor.h \
> mutex.h \
> + ptrace.h \
> sched.h \
> sem.h \
> signal.h \
> diff --git a/include/cobalt/uapi/ptrace.h b/include/cobalt/uapi/ptrace.h
> new file mode 100644
> index 0000000000..4e61d458c1
> --- /dev/null
> +++ b/include/cobalt/uapi/ptrace.h
> @@ -0,0 +1,24 @@
> +/*
> + * Copyright (C) Siemens AG, 2015-2021
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
> + */
> +#ifndef _COBALT_UAPI_PTRACE_H
> +#define _COBALT_UAPI_PTRACE_H
> +
> +#define COBALT_PTRACE_EVENT_STOP 0x1
> +#define COBALT_PTRACE_EVENT_RESUME 0x2
> +
> +#endif /* !_COBALT_UAPI_PTRACE_H */
> diff --git a/include/cobalt/uapi/syscall.h b/include/cobalt/uapi/syscall.h
> index a2795a3644..1fb4009d64 100644
> --- a/include/cobalt/uapi/syscall.h
> +++ b/include/cobalt/uapi/syscall.h
> @@ -128,6 +128,8 @@
> #define sc_cobalt_clock_nanosleep64 105
> #define sc_cobalt_clock_getres64 106
> #define sc_cobalt_clock_adjtime64 107
> +#define sc_cobalt_ptrace_helper_init 108
> +#define sc_cobalt_ptrace_event_wait 109
Just a note that this affects the onoing Y2038 work. Song, we might have
to adjust the syscall numbers again (assuming that Jan is coming in earlier)
>
> #define __NR_COBALT_SYSCALLS 128 /* Power of 2 */
>
> diff --git a/kernel/cobalt/posix/process.c b/kernel/cobalt/posix/process.c
> index 1f059ad830..76514ec82f 100644
> --- a/kernel/cobalt/posix/process.c
> +++ b/kernel/cobalt/posix/process.c
> @@ -45,6 +45,7 @@
> #include <cobalt/kernel/ppd.h>
> #include <cobalt/kernel/vdso.h>
> #include <cobalt/kernel/thread.h>
> +#include <cobalt/uapi/ptrace.h>
> #include <cobalt/uapi/signal.h>
> #include <cobalt/uapi/syscall.h>
> #include <pipeline/sched.h>
> @@ -683,16 +684,25 @@ void cobalt_stop_debugged_process(struct xnthread
> *thread)
> struct cobalt_process *process = process_from_thread(thread);
> struct cobalt_thread *cth;
>
> - if (process->debugged_threads > 0)
> + if (process->stopped_for_ptrace)
> return;
>
> + process->stopped_for_ptrace = true;
> +
> list_for_each_entry(cth, &process->thread_list, next) {
> - if (&cth->threadbase == thread)
> + if (&cth->threadbase == thread ||
> + &cth->threadbase == process->ptrace_helper)
> continue;
>
> xnthread_suspend(&cth->threadbase, XNDBGSTOP, XN_INFINITE,
> XN_RELATIVE, NULL);
> }
> +
> + if (process->ptrace_helper) {
> + process->pending_ptrace_events |= COBALT_PTRACE_EVENT_STOP;
> + if (xnsynch_wakeup_one_sleeper(&process->ptrace_stop_event))
> + xnsched_run();
> + }
> }
>
> static void cobalt_resume_debugged_process(struct cobalt_process *process)
> @@ -705,6 +715,8 @@ static void cobalt_resume_debugged_process(struct
> cobalt_process *process)
> if (xnthread_test_state(&cth->threadbase, XNDBGSTOP))
> xnthread_resume(&cth->threadbase, XNDBGSTOP);
>
> + process->stopped_for_ptrace = false;
> +
> xnsched_unlock();
> }
>
> @@ -728,6 +740,17 @@ void cobalt_unregister_debugged_thread(struct xnthread
> *thread)
> {
> struct cobalt_process *process = process_from_thread(thread);
>
> + if (process->ptrace_helper && process->debugged_threads == 1 &&
> + !(process->pending_ptrace_events & COBALT_PTRACE_EVENT_RESUME)) {
> + process->pending_ptrace_events |= COBALT_PTRACE_EVENT_RESUME;
> + if (process->ptrace_helper != thread) {
> + xnthread_clear_state(thread, XNSSTEP);
> + xnthread_set_state(process->ptrace_helper, XNSSTEP);
> + }
> + xnthread_resume(process->ptrace_helper, XNDBGSTOP);
> + return;
> + }
> +
> process->debugged_threads--;
> xnthread_clear_state(thread, XNSSTEP);
>
> @@ -834,6 +857,7 @@ void cobalt_adjust_affinity(struct task_struct *task) /*
> nklocked, IRQs off */
>
> static void __handle_taskexit_event(struct task_struct *p)
> {
> + struct cobalt_process *process = cobalt_current_process();
> struct cobalt_ppd *sys_ppd;
> struct xnthread *thread;
> spl_t s;
> @@ -850,6 +874,11 @@ static void __handle_taskexit_event(struct task_struct
> *p)
>
> xnlock_get_irqsave(&nklock, s);
>
> + if (process && process->ptrace_helper == thread) {
> + process->ptrace_helper = NULL;
> + process->pending_ptrace_events = 0;
> + }
> +
> if (xnthread_test_state(thread, XNSSTEP))
> cobalt_unregister_debugged_thread(thread);
>
> @@ -1043,6 +1072,7 @@ static void *cobalt_process_attach(void)
> INIT_LIST_HEAD(&process->thread_list);
> xntree_init(&process->usems);
> bitmap_fill(process->timers_map, CONFIG_XENO_OPT_NRTIMERS);
> + xnsynch_init(&process->ptrace_stop_event, XNSYNCH_FIFO, NULL);
> cobalt_set_process(process);
>
> return process;
> diff --git a/kernel/cobalt/posix/process.h b/kernel/cobalt/posix/process.h
> index 279707a680..29294a8f60 100644
> --- a/kernel/cobalt/posix/process.h
> +++ b/kernel/cobalt/posix/process.h
> @@ -22,6 +22,7 @@
> #include <linux/bitmap.h>
> #include <pipeline/thread.h>
> #include <cobalt/kernel/ppd.h>
> +#include <cobalt/kernel/synch.h>
>
> #define NR_PERSONALITIES 4
> #if BITS_PER_LONG < NR_PERSONALITIES
> @@ -55,6 +56,10 @@ struct cobalt_process {
> void *priv[NR_PERSONALITIES];
> int ufeatures;
> unsigned int debugged_threads;
> + struct xnthread *ptrace_helper;
> + struct xnsynch ptrace_stop_event;
> + unsigned int pending_ptrace_events;
> + bool stopped_for_ptrace;
> };
>
> struct cobalt_resnode {
> diff --git a/kernel/cobalt/posix/syscall.c b/kernel/cobalt/posix/syscall.c
> index 618f965c2a..71aa669b88 100644
> --- a/kernel/cobalt/posix/syscall.c
> +++ b/kernel/cobalt/posix/syscall.c
> @@ -22,6 +22,7 @@
> #include <linux/kconfig.h>
> #include <linux/unistd.h>
> #include <cobalt/uapi/corectl.h>
> +#include <cobalt/uapi/ptrace.h>
> #include <cobalt/kernel/tree.h>
> #include <cobalt/kernel/vdso.h>
> #include <cobalt/kernel/init.h>
> @@ -287,6 +288,91 @@ static COBALT_SYSCALL(serialdbg, current,
> return 0;
> }
>
> +static COBALT_SYSCALL(ptrace_helper_init, current, (void))
> +{
> + struct cobalt_process *process = cobalt_current_process();
> + struct xnthread *curr = xnthread_current();
> + int err = 0;
> + spl_t s;
> +
> + if (curr == NULL || process == NULL)
> + return -EPERM;
> +
> + xnlock_get_irqsave(&nklock, s);
> +
> + if (!process->ptrace_helper)
> + process->ptrace_helper = curr;
> + else
> + err = -EBUSY;
> +
> + xnlock_put_irqrestore(&nklock, s);
> +
> + return err;
> +}
> +
> +static COBALT_SYSCALL(ptrace_event_wait, primary, (int event))
> +{
> + struct cobalt_process *process = cobalt_current_process();
> + struct xnthread *curr = xnthread_current();
> + int ret = 0;
> + spl_t s;
> +
> + xnlock_get_irqsave(&nklock, s);
> +
> + if (curr == NULL || process == NULL || process->ptrace_helper != curr) {
> + ret = -EPERM;
> + goto out;
> + }
> +
> + if (event == COBALT_PTRACE_EVENT_STOP) {
> + xnthread_set_state(curr, XNDBGCTRL);
> +
> + if (process->debugged_threads == 1)
> + cobalt_unregister_debugged_thread(curr);
> +
> + if (process->pending_ptrace_events & COBALT_PTRACE_EVENT_STOP) {
> + process->pending_ptrace_events = 0;
> + goto out;
> + }
> +
> + /* Now wait for the next debugging round. */
> + if (xnsynch_sleep_on(&process->ptrace_stop_event, XN_INFINITE,
> + XN_RELATIVE) & XNBREAK)
> + ret = -ERESTARTSYS;
> + else
> + process->pending_ptrace_events = 0;
> + } else if (event == COBALT_PTRACE_EVENT_RESUME) {
> + xnthread_clear_state(curr, XNDBGCTRL);
> +
> + if (process->pending_ptrace_events & COBALT_PTRACE_EVENT_RESUME)
> + goto out;
> +
> + /*
> + * Now wait for the end of the debugging round. If there is
> + * already SIGSTOP pending, interrupt the suspension and relax
> + * the helper thread on return from the syscall. The syscall
> + * will be restarted when the thread resumes, then with
> + * COBALT_PTRACE_EVENT_RESUME pending so that we will not block
> + * again.
> + */
> + if (signal_pending(current)) {
> + ret = -ERESTARTSYS;
> + } else {
> + xnthread_suspend(curr, XNDBGSTOP, XN_INFINITE,
> XN_RELATIVE,
> + NULL);
> + if (xnthread_test_info(curr, XNBREAK))
> + ret = -ERESTARTSYS;
> + }
> + } else {
> + ret = -EINVAL;
> + }
> +
> +out:
> + xnlock_put_irqrestore(&nklock, s);
> +
> + return ret;
> +}
> +
> static void stringify_feature_set(unsigned long fset, char *buf, int size)
> {
> unsigned long feature;
> diff --git a/lib/cobalt/Makefile.am b/lib/cobalt/Makefile.am
> index b3003cd957..3f9136e84c 100644
> --- a/lib/cobalt/Makefile.am
> +++ b/lib/cobalt/Makefile.am
> @@ -24,6 +24,7 @@ libcobalt_la_SOURCES = \
> mutex.c \
> parse_vdso.c \
> printf.c \
> + ptrace.c \
> rtdm.c \
> sched.c \
> select.c \
> diff --git a/lib/cobalt/ptrace.c b/lib/cobalt/ptrace.c
> new file mode 100644
> index 0000000000..f0c305b7b0
> --- /dev/null
> +++ b/lib/cobalt/ptrace.c
> @@ -0,0 +1,91 @@
> +/*
> + * Copyright (C) Siemens AG, 2015-2021
> + *
> + * Authors:
> + * Jan Kiszka <[email protected]>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
> + */
> +#include <pthread.h>
> +#include <cobalt/ptrace.h>
> +#include <asm/xenomai/syscall.h>
> +
> +/**
> + * @ingroup cobalt_api
> + * @defgroup cobalt_ptrace Debugging extension
> + *
> + * Cobalt process debugging extensions
> + *
> + *@{
> + */
> +
> +/**
> + * Register thread as ptrace helper
> + *
> + * Register the calling thread as ptrace debugging helper. Only one thread in
> + * a process can take over this role which will be in effect until the thread
> + * terminates.
> + *
> + * @retval 0 on success;
> + * @retval -1 with @a errno set if:
> + * - EBUSY, another thread is already registered.
> + * - EPERM, caller is not a Cobalt thread.
> + */
> +int cobalt_ptrace_helper_init(void)
> +{
> + int ret;
> +
> + ret = XENOMAI_SYSCALL0(sc_cobalt_ptrace_helper_init);
> + if (ret < 0) {
> + errno = -ret;
> + return -1;
> + }
> +
> + return ret;
> +}
> +
> +/**
> + * Wait on the next ptrace helper event
> + *
> + * Block the caller until the next ptrace even for the helper arrives. The
> + * caller must have been registered as ptrace helper before.
> + *
> + * @param event type of event to wait for, either @a COBALT_PTRACE_EVENT_STOP
> + * or @a COBALT_PTRACE_EVENT_RESUME
> + *
> + * @retval 0 on success;
> + * @retval -1 with @a errno set if:
> + * - EINVAL, invalid @a event.
> + * - EPERM, caller is not a Cobalt thread.
> + */
> +int cobalt_ptrace_event_wait(int event)
> +{
> + int ret, oldtype;
> +
> + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
> +
> + ret = XENOMAI_SYSCALL1(sc_cobalt_ptrace_event_wait, event);
> +
> + pthread_setcanceltype(oldtype, NULL);
> +
> + if (ret < 0) {
> + errno = -ret;
> + return -1;
> + }
> +
> + return ret;
> +}
> +
> +/** @} */
>