From mboxrd@z Thu Jan 1 00:00:00 1970 Subject: Re: [PATCH 2/3] cobalt: Add ptrace debugging helper interface References: <38334cd4bacae85a759591823d6e8898e7d52f76.1625688977.git.jan.kiszka@siemens.com> From: Florian Bezdeka Message-ID: <1b34cb70-ba44-8083-e197-2159f0db48e8@siemens.com> Date: Thu, 8 Jul 2021 09:58:39 +0200 In-Reply-To: <38334cd4bacae85a759591823d6e8898e7d52f76.1625688977.git.jan.kiszka@siemens.com> Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit MIME-Version: 1.0 List-Id: Discussions about the Xenomai project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Jan Kiszka , xenomai@xenomai.org, Song Chen On 07.07.21 22:16, Jan Kiszka via Xenomai wrote: > From: Jan Kiszka > > 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 > --- > 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 > + * > + * 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 > + > +#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 > #include > #include > +#include > #include > #include > #include > @@ -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 > #include > #include > +#include > > #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 > #include > #include > +#include > #include > #include > #include > @@ -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 > + * > + * 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 > +#include > +#include > + > +/** > + * @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; > +} > + > +/** @} */ >