Make debugging more deterministic
cover Make debugging more deterministic
These patches are sticking in my queue for more than 3 years now which is bad. The good thing is that they were able to mature, at least on x86, over that time because we are actually using them.
Two issues are addressed with these changes:
1. Ensure that threads that were stopped for debugging in primary mode will also be resumed in that mode. This prevents deviating behavior compared to non-debugged execution.
2. If one thread in a process hits a debugging condition and is about to bring the whole process into stopped state, ensure that all other RT threads are stopped immediately. This prevents that, because the interrupted thread first needs to go to Linux in order to start the process stop, other threads can continue running too far, changing the process state so that debugging becomes harder if not impossible.
To provide these properties, the core needs two small new hooks from I-pipe. Separate patches will be sent against 4.14 that provide these, so far for x86 and ARM only.
Furthermore, this series likely depends on the mayday cleanup I sent earlier, at least it was never used without that (you don't want to debug without it). That also raises the question how to manage the I-pipe feature dependencies best in order work around or simply exclude old I-pipe releases.
Please review carefully if the design and implementation make sense. As I said, we are using it, so it is not completely broken. But I may have overseen some ugly corner cases nevertheless.
Unless too many issues surface, I would like to get this feature into master before 3.1, along with the needed hooks in I-pipe (including active stable trees).
Jan
Jan Kiszka (7):
cobalt/kernel: Rework register/unregister_debugged_thread cobalt/kernel: Switch back ptraced primary mode threads on resume cobalt/kernel: Reintroduce process-local thread list cobalt/kernel: Introduce XNDBGSTOP
cobalt/kernel: Implement synchronous stop and resume for process
debugging
cobalt/kernel: Clear mayday request of relaxed task testsuite/smokey: gdb-based debugging test
configure.ac | 1 + include/cobalt/kernel/thread.h | 2 +- include/cobalt/uapi/kernel/thread.h | 5 +- kernel/cobalt/posix/process.c | 218 ++++++++++++++++++++++++- kernel/cobalt/posix/process.h | 4 +- kernel/cobalt/posix/thread.c | 4 +- kernel/cobalt/posix/thread.h | 2 +- kernel/cobalt/thread.c | 18 ++- testsuite/smokey/Makefile.am | 1 + testsuite/smokey/gdb/Makefile.am | 13 ++ testsuite/smokey/gdb/gdb.c | 310 ++++++++++++++++++++++++++++++++++++ 11 files changed, 564 insertions(+), 14 deletions(-) create mode 100644 testsuite/smokey/gdb/Makefile.am create mode 100644 testsuite/smokey/gdb/gdb.c
Signature · 1 line
2.16.4
RFC PATCH 1/7 cobalt/kernel: Rework register/unregister_debugged_thread
From: Jan Kiszka <[email protected]>
Given that registration can run asynchronously to deregistration, better put testing for XNSSTEP and the latter under nklock. This is likely overkill for task termination but it's more consistent.
While at it, move up the functions and add a cobalt_ prefix. We will need them earlier in the file and are going to export one of them for use in other code modules.
--- kernel/cobalt/posix/process.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-)
kernel/cobalt/posix/process.c +28 -6
@@ -720,6 +720,18 @@ int cobalt_map_user(struct xnthread *thread, __u32 __user *u_winoff)return 0 ; } +/* called with nklock held */ +static void cobalt_register_debugged_thread (struct xnthread *thread) +{ +xnthread_set_state (thread,XNSSTEP ); +} + +/* called with nklock held */ +static void cobalt_unregister_debugged_thread (struct xnthread *thread) +{ +xnthread_clear_state (thread,XNSSTEP ); +} +static inline int handle_exception (struct ipipe_trap_data *d) {struct xnthread *thread;
@@ -1016,6 +1028,7 @@ static void __handle_taskexit_event(struct task_struct *p) {struct cobalt_ppd *sys_ppd;struct xnthread *thread; +spl_t s;/* * We are called for both kernel and user shadows over the
@@ -1027,6 +1040,13 @@ static void __handle_taskexit_event(struct task_struct *p)XENO_BUG_ON (COBALT , thread ==NULL );trace_cobalt_shadow_unmap (thread); +xnlock_get_irqsave (&nklock, s); + +if (xnthread_test_state (thread,XNSSTEP )) +cobalt_unregister_debugged_thread (thread); + +xnlock_put_irqrestore (&nklock, s); +xnthread_run_handler_stack (thread, exit_thread);xnsched_run ();
@@ -1084,6 +1104,8 @@ static int handle_schedule_event(struct task_struct *next_task)if (next ==NULL ) goto out; +xnlock_get_irqsave (&nklock, s); +/* * Track tasks leaving the ptraced state. Check both SIGSTOP * (NPTL) and SIGINT (LinuxThreads) to detect ptrace
@@ -1102,15 +1124,15 @@ static int handle_schedule_event(struct task_struct *next_task) &next_task->signal->shared_pending.signal);if (sigismember (&pending,SIGSTOP ) ||sigismember (&pending,SIGINT )) - goto check; + goto no_ptrace; } -xnlock_get_irqsave (&nklock, s); -xnthread_clear_state (next,XNSSTEP ); -xnlock_put_irqrestore (&nklock, s); +cobalt_unregister_debugged_thread (next);xnthread_set_localinfo (next,XNHICCUP ); } -check: +no_ptrace: +xnlock_put_irqrestore (&nklock, s); +/* * Do basic sanity checks on the incoming thread state. * NOTE: we allow ptraced threads to run shortly in order to
@@ -1163,7 +1185,7 @@ static int handle_sigwake_event(struct task_struct *p)if (sigismember (&pending,SIGTRAP ) ||sigismember (&pending,SIGSTOP ) ||sigismember (&pending,SIGINT )) -xnthread_set_state (thread,XNSSTEP ); +cobalt_register_debugged_thread (thread); }if (xnthread_test_state (thread,XNRELAX )) {
Signature · 1 line
2.16.4
RFC PATCH 2/7 cobalt/kernel: Switch back ptraced primary mode threads on resume
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
When a thread is stopped in primary mode for debugging, make sure it will migrate back before resuming in user space. This is a building block for making real-time process debugging more deterministic.
The information if a thread should resume in primary is transported via a new thread info flag XNCONTHI. It is set either by the exception handler detecting a breakpoint hit in primary user mode or by xnthread_relax when invoked for a thread under XNSSTEP which indicates it was signaled to stop.
The feature depends on the new I-pipe notifier for user interrupt return, i.e. a callback from the point that a secondary mode thread is about to return to user space after an exception or an interrupt.
--- include/cobalt/uapi/kernel/thread.h | 1 + kernel/cobalt/posix/process.c | 68 ++++++++++++++++++++++++++++++++++++- kernel/cobalt/thread.c | 12 +++++++ 3 files changed, 80 insertions(+), 1 deletion(-)
include/cobalt/uapi/kernel/thread.h +1 -0
@@ -72,6 +72,7 @@#define XNCANCELD 0x00000040/**< Cancellation request is pending */ #define XNPIALERT 0x00000080/**< Priority inversion alert (SIGDEBUG sent) */ #define XNSCHEDP 0x00000100/**< schedparam propagation is pending */ +#define XNCONTHI 0x00000200/**< Continue in primary mode after debugging */ /* Local information flags (private to current thread) */
kernel/cobalt/posix/process.c +67 -1
100 lines not on this page · raw message
kernel/cobalt/thread.c +12 -0
@@ -2100,6 +2100,18 @@ void xnthread_relax(int notify, int reason)* dropped by xnthread_suspend(). */ xnlock_get (&nklock); +#ifdef IPIPE_KEVT_USERINTRET +/* +* If the thread is being debugged, record that it should migrate back +* in case it resumes in userspace. If it resumes in kernel space, i.e. +* over a restarting syscall, the associated hardening will both clear +* XNCONTHI and disable the user return notifier again. +*/ +if (xnthread_test_state (thread,XNSSTEP )) { +xnthread_set_info (thread,XNCONTHI ); +ipipe_enable_user_intret_notifier (); + } +#endif set_current_state (p->state & ~TASK_NOWAKEUP );xnthread_run_handler_stack (thread, relax_thread);xnthread_suspend (thread,XNRELAX ,XN_INFINITE ,XN_RELATIVE ,NULL );
Signature · 1 line
2.16.4
RFC PATCH 3/7 cobalt/kernel: Reintroduce process-local thread list
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
Track threads on a per-process basis again to make walking through them cheaper.
--- kernel/cobalt/posix/process.c | 3 ++- kernel/cobalt/posix/process.h | 3 ++- kernel/cobalt/posix/thread.c | 4 +++- kernel/cobalt/posix/thread.h | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-)
kernel/cobalt/posix/process.c +2 -1
@@ -83,7 +83,7 @@ struct xnthread_personality *cobalt_personalities[NR_PERSONALITIES];static struct xnsynch yield_sync; -LIST_HEAD (cobalt_thread_list); +LIST_HEAD (cobalt_global_thread_list);struct cobalt_resources cobalt_global_resources = { .condq =LIST_HEAD_INIT (cobalt_global_resources.condq),
@@ -1434,6 +1434,7 @@ static void *cobalt_process_attach(void)INIT_LIST_HEAD (&process->resources.eventq);INIT_LIST_HEAD (&process->resources.schedq);INIT_LIST_HEAD (&process->sigwaiters); +INIT_LIST_HEAD (&process->thread_list);xntree_init (&process->usems);bitmap_fill (process->timers_map,CONFIG_XENO_OPT_NRTIMERS );cobalt_set_process (process);
kernel/cobalt/posix/process.h +2 -1
@@ -51,6 +51,7 @@ struct cobalt_process {struct rb_root usems;struct list_head sigwaiters;struct cobalt_resources resources; +struct list_head thread_list;DECLARE_BITMAP (timers_map,CONFIG_XENO_OPT_NRTIMERS );struct cobalt_timer *timers[CONFIG_XENO_OPT_NRTIMERS ];void *priv[NR_PERSONALITIES ];
@@ -86,7 +87,7 @@ int cobalt_yield(xnticks_t min, xnticks_t max);int cobalt_process_init (void ); -extern struct list_head cobalt_thread_list; +extern struct list_head cobalt_global_thread_list;extern struct cobalt_resources cobalt_global_resources;
kernel/cobalt/posix/thread.c +3 -1
@@ -359,6 +359,7 @@ static int pthread_create(struct cobalt_thread **thread_p,const struct sched_param_ex *param_ex, struct task_struct *task) { + struct cobalt_process *process = cobalt_current_process();struct xnsched_class *sched_class;union xnsched_policy_param param;struct xnthread_init_attr iattr;
@@ -413,7 +414,8 @@ static int pthread_create(struct cobalt_thread **thread_p, }xnlock_get_irqsave (&nklock, s); -list_add_tail (&thread->next, &cobalt_thread_list); +list_add_tail (&thread->next, process ? &process->thread_list + : &cobalt_global_thread_list);xnlock_put_irqrestore (&nklock, s); thread->hkey.u_pth =0 ;
kernel/cobalt/posix/thread.h +1 -1
@@ -91,7 +91,7 @@ struct cobalt_thread {struct xnthread threadbase;struct cobalt_extref extref;struct cobalt_process *process; -struct list_head next;/* in cobalt_thread_list */ +struct list_head next;/* in global/process thread_list */ /** Signal management. */ sigset_t sigpending;
Signature · 1 line
2.16.4
RFC PATCH 4/7 cobalt/kernel: Introduce XNDBGSTOP
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
This Introduces an additional blocking state for threads. It is used to hold back threads after resumption by the debugger in primary mode for coordinated continuation.
--- include/cobalt/kernel/thread.h | 2 +- include/cobalt/uapi/kernel/thread.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-)
include/cobalt/kernel/thread.h +1 -1
@@ -38,7 +38,7 @@* @addtogroup cobalt_core_thread * @{ */ -#define XNTHREAD_BLOCK_BITS (XNSUSP|XNPEND|XNDELAY|XNDORMANT|XNRELAX|XNMIGRATE|XNHELD) +#define XNTHREAD_BLOCK_BITS (XNSUSP|XNPEND|XNDELAY|XNDORMANT|XNRELAX|XNMIGRATE|XNHELD|XNDBGSTOP)#define XNTHREAD_MODE_BITS (XNRRB|XNWARN|XNTRAPLB)struct xnthread ;
include/cobalt/uapi/kernel/thread.h +3 -1
@@ -51,6 +51,7 @@#define XNJOINED 0x00080000/**< Another thread waits for joining this thread */ #define XNTRAPLB 0x00100000/**< Trap lock break (i.e. may not sleep with sched lock) */ #define XNDEBUG 0x00200000/**< User-level debugging enabled */ +#define XNDBGSTOP 0x00400000/**< Stopped for synchronous debugging */ /** @} */
@@ -101,8 +102,9 @@* 'r' -> Undergoes round-robin. * 't' -> Runtime mode errors notified. * 'L' -> Lock breaks trapped. +* 's' -> Ptraced, stopped synchronously. */ -#define XNTHREAD_STATE_LABELS "SWDRU..X.HbTlrt.....L." +#define XNTHREAD_STATE_LABELS "SWDRU..X.HbTlrt.....L.s"struct xnthread_user_window {__u32 state;
Signature · 1 line
2.16.4
RFC PATCH 5/7 cobalt/kernel: Implement synchronous stop and resume for process debugging
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
When a thread in primary mode currently hits a breakpoint, it will immediately be relaxed, leaving space for other runnable RT threads of the process to take over. This can delay gdb to finally take control and stop the whole process.
To prevent this unexpected and often undesired behavior, this introduces a mechanism to stop all threads of a process in primary mode when one of them runs into a breakpoint or a single-step event. The new XNDBGSTOP thread state is used for this purpose.
On resumption, no thread can continue in primary mode until all that should do so have reached this state. Then the last one will wake up all threads waiting on XNDBGSTOP.
--- kernel/cobalt/posix/process.c | 131 +++++++++++++++++++++++++++++++++++++++--- kernel/cobalt/posix/process.h | 1 + kernel/cobalt/thread.c | 4 +- 3 files changed, 126 insertions(+), 10 deletions(-)
kernel/cobalt/posix/process.c +122 -9
@@ -94,6 +94,12 @@ struct cobalt_resources cobalt_global_resources = { .schedq = LIST_HEAD_INIT(cobalt_global_resources.schedq), }; +static inline struct cobalt_process * +process_from_thread (struct xnthread *thread) +{ + return container_of(thread, struct cobalt_thread, threadbase)->process; +} +static unsigned __attribute__((pure))process_hash_crunch (struct mm_struct *mm) {unsigned long hash = ((unsigned long )mm -PAGE_OFFSET ) /sizeof (*mm);
@@ -720,16 +726,73 @@ int cobalt_map_user(struct xnthread *thread, __u32 __user *u_winoff)return 0 ; } +#ifdef IPIPE_KEVT_PTRESUME +static void stop_debugged_process (struct xnthread *thread) +{ +struct cobalt_process *process =process_from_thread (thread); +struct cobalt_thread *cth; + +if (process->debugged_threads >0 ) +return ; + +list_for_each_entry (cth, &process->thread_list, next) { +if (&cth->threadbase == thread) +continue ; + +xnthread_suspend (&cth->threadbase,XNDBGSTOP ,XN_INFINITE , +XN_RELATIVE ,NULL ); + } +} + +static void resume_debugged_process (struct cobalt_process *process) +{ +struct cobalt_thread *cth; + +xnsched_lock (); + +list_for_each_entry (cth, &process->thread_list, next) +if (xnthread_test_state (&cth->threadbase,XNDBGSTOP )) +xnthread_resume (&cth->threadbase,XNDBGSTOP ); + +xnsched_unlock (); +} + +#else /* IPIPE_KEVT_PTRESUME unavailable */ + +static inline void stop_debugged_process (struct xnthread *thread) +{ +} + +static inline void resume_debugged_process (struct cobalt_process *process) +{ +} +#endif /* IPIPE_KEVT_PTRESUME unavailable */ +/* called with nklock held */ static void cobalt_register_debugged_thread (struct xnthread *thread) { +struct cobalt_process *process =process_from_thread (thread); +xnthread_set_state (thread,XNSSTEP ); + +stop_debugged_process (thread); + process->debugged_threads++; + +if (xnthread_test_state (thread,XNRELAX )) +xnthread_suspend (thread,XNDBGSTOP ,XN_INFINITE ,XN_RELATIVE , +NULL ); }/* called with nklock held */ static void cobalt_unregister_debugged_thread (struct xnthread *thread) { +struct cobalt_process *process =process_from_thread (thread); + + process->debugged_threads--;xnthread_clear_state (thread,XNSSTEP ); + +if (process->debugged_threads ==0 ) +resume_debugged_process (process); }static inline int handle_exception (struct ipipe_trap_data *d)
@@ -740,23 +803,25 @@ static inline int handle_exception(struct ipipe_trap_data *d) sched =xnsched_current (); thread = sched->curr; -if (xnthread_test_state (thread,XNROOT )) -return 0 ; - -trace_cobalt_thread_fault (d); -#ifdef IPIPE_KEVT_USERINTRET if (xnarch_fault_bp_p (d) &&user_mode (d->regs)) {spl_t s;xnlock_get_irqsave (&nklock, s); -xnthread_set_info (thread,XNCONTHI ); +if (!xnthread_test_state (thread,XNRELAX )) { +xnthread_set_info (thread,XNCONTHI ); +ipipe_enable_user_intret_notifier (); + } +stop_debugged_process (thread);xnlock_put_irqrestore (&nklock, s); - -ipipe_enable_user_intret_notifier (); }#endif +if (xnthread_test_state (thread,XNROOT )) +return 0 ; + +trace_cobalt_thread_fault (d); +if (xnarch_fault_fpu_p (d)) {#ifdef CONFIG_XENO_ARCH_FPU spl_t s;
@@ -991,6 +1056,10 @@ void ipipe_migration_hook(struct task_struct *p) /* hw IRQs off */ }#endif +/* Unregister as debugged thread in case we postponed this. */ +if (unlikely (xnthread_test_state (thread,XNSSTEP ))) +cobalt_unregister_debugged_thread (thread); +xnlock_put (&nklock);xnsched_run ();
@@ -1151,7 +1220,15 @@ static int handle_schedule_event(struct task_struct *next_task) sigismember(&pending, SIGINT)) goto no_ptrace; } -cobalt_unregister_debugged_thread (next); + +/* +* Do not unregister before the thread migrated. +* cobalt_unregister_debugged_thread will then be called by our +* ipipe_migration_hook. +*/ +if (!xnthread_test_info (next,XNCONTHI )) +cobalt_unregister_debugged_thread (next); +xnthread_set_localinfo (next,XNHICCUP ); }
@@ -1230,6 +1307,13 @@ static int handle_sigwake_event(struct task_struct *p)if (p->state & (TASK_INTERRUPTIBLE |TASK_UNINTERRUPTIBLE ))cobalt_set_task_state (p, p->state |TASK_NOWAKEUP ); +/* +* Allow a thread stopped for debugging to resume briefly in order to +* migrate to secondary mode. xnthread_relax will reapply XNDBGSTOP. +*/ +if (xnthread_test_state (thread,XNDBGSTOP )) +xnthread_resume (thread,XNDBGSTOP ); +__xnthread_kick (thread);xnsched_run ();
@@ -1335,6 +1419,30 @@ static int handle_user_return(struct task_struct *task) }#endif /* IPIPE_KEVT_USERINTRET */ +#ifdef IPIPE_KEVT_PTRESUME +int handle_ptrace_resume (struct ipipe_ptrace_resume_data *resume) +{ +struct xnthread *thread; +spl_t s; + + thread =xnthread_from_task (resume->task); +if (thread ==NULL ) +return KEVENT_PROPAGATE ; + +if (resume->request ==PTRACE_SINGLESTEP && +xnthread_test_state (thread,XNSSTEP )) { +xnlock_get_irqsave (&nklock, s); + +xnthread_resume (thread,XNDBGSTOP ); +cobalt_unregister_debugged_thread (thread); + +xnlock_put_irqrestore (&nklock, s); + } + +return KEVENT_PROPAGATE ; +} +#endif /* IPIPE_KEVT_PTRESUME */ +int ipipe_kevent_hook (int kevent,void *data) {int ret;
@@ -1367,6 +1475,11 @@ int ipipe_kevent_hook(int kevent, void *data)case IPIPE_KEVT_USERINTRET : ret =handle_user_return (data);break ; +#endif +#ifdef IPIPE_KEVT_PTRESUME +case IPIPE_KEVT_PTRESUME : + ret =handle_ptrace_resume (data); +break ;#endif default : ret =KEVENT_PROPAGATE ;
kernel/cobalt/posix/process.h +1 -0
@@ -56,6 +56,7 @@ struct cobalt_process {struct cobalt_timer *timers[CONFIG_XENO_OPT_NRTIMERS ];void *priv[NR_PERSONALITIES ];int ufeatures; +unsigned int debugged_threads; };struct cobalt_resnode {
kernel/cobalt/thread.c +3 -1
@@ -2070,6 +2070,7 @@ void xnthread_relax(int notify, int reason) {struct xnthread *thread =xnthread_current ();struct task_struct *p = current; +int suspension =XNRELAX ;int cpu__maybe_unused ;siginfo_t si;
@@ -2110,11 +2111,12 @@ void xnthread_relax(int notify, int reason)if (xnthread_test_state (thread,XNSSTEP )) {xnthread_set_info (thread,XNCONTHI );ipipe_enable_user_intret_notifier (); + suspension |=XNDBGSTOP ; }#endif set_current_state (p->state & ~TASK_NOWAKEUP );xnthread_run_handler_stack (thread, relax_thread); -xnthread_suspend (thread,XNRELAX ,XN_INFINITE ,XN_RELATIVE ,NULL ); +xnthread_suspend (thread, suspension,XN_INFINITE ,XN_RELATIVE ,NULL );splnone ();/*
Signature · 1 line
2.16.4
RFC PATCH 6/7 cobalt/kernel: Clear mayday request of relaxed task
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
If a task relaxes on its own, before processing a mayday request, clear this request to avoid redundant calls of the machinery. This is particularly important for ensuring XNCONTHI works as expected after ptrace resumption.
--- kernel/cobalt/thread.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/cobalt/thread.c +2 -0
@@ -2174,6 +2174,8 @@ void xnthread_relax(int notify, int reason) }#endif +ipipe_clear_thread_flag (TIP_MAYDAY ); +trace_cobalt_shadow_relaxed (thread); }EXPORT_SYMBOL_GPL (xnthread_relax);
Signature · 1 line
2.16.4
RFC PATCH 7/7 testsuite/smokey: gdb-based debugging test
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
Add a test case for advanced gdb-based debugging of real-time processes. It currently checks three aspects:
- Do we resume an RT thread in primary mode after hitting a breakpoint in that mode?
- Do we stop all RT threads immediately when one process thread hits a breakpoint?
- Do we resume all threads then synchronously (at when running on the same CPU) when the program continues?
The test first forks a gdb instance that will start the test application again, but with different command line options so that the second run executes the inner test logic. The original instance controls gdb via local pipes and evaluates the debugger output.
--- configure.ac | 1 + testsuite/smokey/Makefile.am | 1 + testsuite/smokey/gdb/Makefile.am | 13 ++ testsuite/smokey/gdb/gdb.c | 310 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 325 insertions(+) create mode 100644 testsuite/smokey/gdb/Makefile.am create mode 100644 testsuite/smokey/gdb/gdb.c
configure.ac +1 -0
@@ -973,6 +973,7 @@ AC_CONFIG_FILES([ \ testsuite/smokey/net_packet_raw/Makefile \ testsuite/smokey/net_common/Makefile \ testsuite/smokey/cpu-affinity/Makefile \ + testsuite/smokey/gdb/Makefile \ testsuite/clocktest/Makefile \ testsuite/xeno-test/Makefile \ utils/Makefile \
testsuite/smokey/Makefile.am +1 -0
@@ -14,6 +14,7 @@ COBALT_SUBDIRS = \ bufp \ cpu-affinity \ fpu-stress \ + gdb \ iddp \ leaks \ memory-coreheap \
testsuite/smokey/gdb/Makefile.am new file +13 -0
@@ -0,0 +1,13 @@ + +noinst_LIBRARIES = libgdb.a + +libgdb_a_SOURCES = gdb.c + +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC) + +libgdb_a_CPPFLAGS = \ + @XENO_USER_CFLAGS@ \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include + +libgdb_a_CXXFLAGS = -g -O0
testsuite/smokey/gdb/gdb.c new file +310 -0
310 lines not on this page · raw message
Signature · 1 line
2.16.4
Quoted 47 lines · Jan Kiszka
These patches are sticking in my queue for more than 3 years now which is bad. The good thing is that they were able to mature, at least on x86, over that time because we are actually using them.
Two issues are addressed with these changes:
1. Ensure that threads that were stopped for debugging in primary mode will also be resumed in that mode. This prevents deviating behavior compared to non-debugged execution.
2. If one thread in a process hits a debugging condition and is about to bring the whole process into stopped state, ensure that all other RT threads are stopped immediately. This prevents that, because the interrupted thread first needs to go to Linux in order to start the process stop, other threads can continue running too far, changing the process state so that debugging becomes harder if not impossible.
To provide these properties, the core needs two small new hooks from I-pipe. Separate patches will be sent against 4.14 that provide these, so far for x86 and ARM only.
Furthermore, this series likely depends on the mayday cleanup I sent earlier, at least it was never used without that (you don't want to debug without it). That also raises the question how to manage the I-pipe feature dependencies best in order work around or simply exclude old I-pipe releases.
Please review carefully if the design and implementation make sense. As I said, we are using it, so it is not completely broken. But I may have overseen some ugly corner cases nevertheless.
Unless too many issues surface, I would like to get this feature into master before 3.1, along with the needed hooks in I-pipe (including active stable trees).
Jan
Jan Kiszka (7):
cobalt/kernel: Rework register/unregister_debugged_thread cobalt/kernel: Switch back ptraced primary mode threads on resume cobalt/kernel: Reintroduce process-local thread list cobalt/kernel: Introduce XNDBGSTOP
cobalt/kernel: Implement synchronous stop and resume for process
debugging
cobalt/kernel: Clear mayday request of relaxed task testsuite/smokey: gdb-based debugging test
Quoted 15 lines
configure.ac | 1 + include/cobalt/kernel/thread.h | 2 +- include/cobalt/uapi/kernel/thread.h | 5 +- kernel/cobalt/posix/process.c | 218 ++++++++++++++++++++++++- kernel/cobalt/posix/process.h | 4 +- kernel/cobalt/posix/thread.c | 4 +- kernel/cobalt/posix/thread.h | 2 +- kernel/cobalt/thread.c | 18 ++- testsuite/smokey/Makefile.am | 1 + testsuite/smokey/gdb/Makefile.am | 13 ++ testsuite/smokey/gdb/gdb.c | 310 ++++++++++++++++++++++++++++++++++++ 11 files changed, 564 insertions(+), 14 deletions(-) create mode 100644 testsuite/smokey/gdb/Makefile.am create mode 100644 testsuite/smokey/gdb/gdb.c
Ack. The pipeline related bits are in -noarch already.
Signature · 1 line
Philippe.
Quoted 64 lines · Philippe Gerum via Xenomai
Quoted 59 lines · Jan Kiszka
These patches are sticking in my queue for more than 3 years now which is bad. The good thing is that they were able to mature, at least on x86, over that time because we are actually using them.
Two issues are addressed with these changes:
1. Ensure that threads that were stopped for debugging in primary mode will also be resumed in that mode. This prevents deviating behavior compared to non-debugged execution.
2. If one thread in a process hits a debugging condition and is about to bring the whole process into stopped state, ensure that all other RT threads are stopped immediately. This prevents that, because the interrupted thread first needs to go to Linux in order to start the process stop, other threads can continue running too far, changing the process state so that debugging becomes harder if not impossible.
To provide these properties, the core needs two small new hooks from I-pipe. Separate patches will be sent against 4.14 that provide these, so far for x86 and ARM only.
Furthermore, this series likely depends on the mayday cleanup I sent earlier, at least it was never used without that (you don't want to debug without it). That also raises the question how to manage the I-pipe feature dependencies best in order work around or simply exclude old I-pipe releases.
Please review carefully if the design and implementation make sense. As I said, we are using it, so it is not completely broken. But I may have overseen some ugly corner cases nevertheless.
Unless too many issues surface, I would like to get this feature into master before 3.1, along with the needed hooks in I-pipe (including active stable trees).
Jan
Jan Kiszka (7):
cobalt/kernel: Rework register/unregister_debugged_thread cobalt/kernel: Switch back ptraced primary mode threads on resume cobalt/kernel: Reintroduce process-local thread list cobalt/kernel: Introduce XNDBGSTOP
cobalt/kernel: Implement synchronous stop and resume for process
debugging
cobalt/kernel: Clear mayday request of relaxed task testsuite/smokey: gdb-based debugging testconfigure.ac | 1 + include/cobalt/kernel/thread.h | 2 +- include/cobalt/uapi/kernel/thread.h | 5 +- kernel/cobalt/posix/process.c | 218 ++++++++++++++++++++++++- kernel/cobalt/posix/process.h | 4 +- kernel/cobalt/posix/thread.c | 4 +- kernel/cobalt/posix/thread.h | 2 +- kernel/cobalt/thread.c | 18 ++- testsuite/smokey/Makefile.am | 1 + testsuite/smokey/gdb/Makefile.am | 13 ++ testsuite/smokey/gdb/gdb.c | 310 ++++++++++++++++++++++++++++++++++++ 11 files changed, 564 insertions(+), 14 deletions(-)Quoted 3 lines
create mode 100644 testsuite/smokey/gdb/Makefile.am create mode 100644
testsuite/smokey/gdb/gdb.c
Quoted 2 lines
Ack. The pipeline related bits are in -noarch already.
Thanks for the review. Rebased (was clean) and applied to next now.
Jan
Signature · 2 lines
Siemens AG, Corporate Technology, CT RDA IOT SES-DE Corporate Competence Center Embedded Linux
PATCH 7/7 testsuite/smokey: gdb: Catch dup and write errors
In reply to Jan KiszkaReported by compiler settings in travis.
---
There is still one breakage remaining, arm on legacy 4.4, not critical for 3.1 likely, but I will fix that soon as well.
testsuite/smokey/gdb/gdb.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-)
testsuite/smokey/gdb/gdb.c +16 -6
@@ -104,7 +104,10 @@ static void wait_for_pattern(const char *string)static void send_command (const char *string,int wait_for_prompt) { -write (pipe_in[1 ], string,strlen (string)); +int err; + + err =write (pipe_in[1 ], string,strlen (string)); +check_no_error ("write(pipe_in)" , err);if (wait_for_prompt)wait_for_pattern ("(gdb) " ); }
@@ -142,8 +145,12 @@ static void eval_expression_inner(const char *fn, int line,const char *expression, const char *expected_value) { - write(pipe_in[1], "p ", 2); -write (pipe_in[1 ], expression,strlen (expression)); + int err; + + err =write (pipe_in[1 ],"p " ,2 ); +check_no_error ("write(pipe_in)" , err); + err =write (pipe_in[1 ], expression,strlen (expression)); +check_no_error ("write(pipe_in)" , err);send_command ("\n" ,0 );check_output (fn, line, expression,"$" );
@@ -261,13 +268,16 @@ static int run_gdb(struct smokey_test *t, int argc, char *const argv[])case 0 :/* redirect input */ close (0 ); -dup (pipe_in[0 ]); + err =dup (pipe_in[0 ]); +check_no_error ("dup(pipe_in)" , err);/* redirect output and stderr */ close (1 ); -dup (pipe_out[1 ]); + err =dup (pipe_out[1 ]); +check_no_error ("dup(pipe_out)" , err);close (2 ); -dup (1 ); + err =dup (1 ); +check_no_error ("dup(1)" , err);snprintf (run_param,sizeof (run_param),"--run=%d" , t->__reserved.id);
Signature · 1 line
2.16.4