Skip to contentSkip to conversation

Make debugging more deterministic

RFC PATCH In review Jan Kiszka 2 participants 11 messages Nov 30, 2018 – Jun 4, 2019

lore
Share & tools
lore

Jan Kiszka

author
#0 lore raw

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
author
#1 lore raw

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
index 10a9fb5678..f4ebf194f6 100644
@@ -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
author
#2 lore raw

RFC PATCH 2/7 cobalt/kernel: Switch back ptraced primary mode threads on resume

In reply to Jan Kiszka

From: 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
index f583ae3854..04982ee0e4 100644
@@ -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
index 1a069d07c9..902a9b8f1a 100644
@@ -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
author
#3 lore raw

RFC PATCH 3/7 cobalt/kernel: Reintroduce process-local thread list

In reply to Jan Kiszka

From: 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
index 1e679762aa..1b89ab7580 100644
@@ -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
index ad1f4c56bb..f867b8a919 100644
@@ -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
index 44f655a300..30c31bb6d9 100644
@@ -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
index 5cc098afab..fec9d17859 100644
@@ -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
author
#4 lore raw

RFC PATCH 4/7 cobalt/kernel: Introduce XNDBGSTOP

In reply to Jan Kiszka

From: 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
index f1f06ff0a6..03adb25296 100644
@@ -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
index 04982ee0e4..0802a16dcd 100644
@@ -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
author
#5 lore raw

RFC PATCH 5/7 cobalt/kernel: Implement synchronous stop and resume for process debugging

In reply to Jan Kiszka

From: 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
index 1b89ab7580..e748c4fbdf 100644
@@ -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
index f867b8a919..375b4cbbb8 100644
@@ -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
index 902a9b8f1a..6597749a20 100644
@@ -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
author
#6 lore raw

RFC PATCH 6/7 cobalt/kernel: Clear mayday request of relaxed task

In reply to Jan Kiszka

From: 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
index 6597749a20..bb1eda1052 100644
@@ -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
author
#7 lore raw

RFC PATCH 7/7 testsuite/smokey: gdb-based debugging test

In reply to Jan Kiszka

From: 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
index e2a2d47cfa..0aa46a2e5e 100644
@@ -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
index 4faf55b0ba..b35b00d82d 100644
@@ -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
new file mode 100644
index 0000000000..987d6ee2d5
@@ -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
#8 lore raw
In reply to Jan Kiszka
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.
author
#9 lore raw
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 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(-)
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
author
#10 lore raw

PATCH 7/7 testsuite/smokey: gdb: Catch dup and write errors

In reply to Jan Kiszka

Reported 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
index a0308d9b2e..fc843cbbbd 100644
@@ -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