evl: ptrace continue without hardware singlestepping support

Tobias Schaffner <[email protected]>
Newsgroups dev.linux.lists.xenomai
Message-ID <[email protected]>
Hi Philippe,

the RISC-V port has an open issue regarding OOB synchronization of 
threads in the EVL ptrace implementation, which leads to a deadlock when 
running the ptrace-sync test.

RISC-V currently does not support hardware-based single stepping, and 
GDB’s behavior on `continue` with multiple threads is different.

If hardware single stepping is available, GDB single-steps the hitter 
with PTRACE_SINGLESTEP and continues the other threads with PTRACE_CONT. 
All threads are continued and can pass the evl_wait_event() barrier in 
handle_ptstop_event(). The hitter breaks again and is finally continued.

In contrast, if hardware single stepping is not available, GDB adds a 
breakpoint to the next instruction, wakes only the hitter with 
PTRACE_CONT, and waits for it to make its step before waking its 
siblings. However, the hitter is not able to make its step because it 
waits in the kernel for its siblings in evl_wait_event(), which are not 
going to be woken by GDB until the hitter makes its step.

The attached strace files show the behavior of GDB on both architectures 
when continuing the breaking worker thread in `ptrace.c`.

Jan already pointed out that there may be a general problem with 
expecting GDB to always continue all threads, and I think he is right. 
Changing the scheduler-locking mode in All-Stop Mode [1], as well as 
switching to Non-Stop Mode [2], should lead to similar issues.

Do you have a suggestion how to solve this?

Best,
Tobias


[1] 
https://sourceware.org/gdb/current/onlinedocs/gdb.html/All_002dStop-Mode.html
[2] 
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Non_002dStop-Mode.html
strace-riscv64 (text/plain, 314.8 KB) - not displayed
strace-amd64 (text/plain, 361.2 KB) - not displayed
ptrace.c (text/x-csrc, 831 B)
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

static pthread_barrier_t ready;

static volatile int bp_dummy;

static void __attribute__((noinline)) bp(void)
{
	bp_dummy++;
}

static void *worker(void *arg)
{
	int id = (int)(long)arg;
	pthread_barrier_wait(&ready);
	printf("worker %d: running\n", id);
	if (id == 1) {
		printf("worker %d: calling bp()\n", id);
		bp();
		printf("worker %d: done\n", id);
	}
	return NULL;
}

int main(void)
{
	pthread_t t1, t2;

	pthread_barrier_init(&ready, NULL, 3);
	pthread_create(&t1, NULL, worker, (void *)1L);
	pthread_create(&t2, NULL, worker, (void *)2L);

	pthread_barrier_wait(&ready);

	printf("main: waiting\n");

	pthread_join(t1, NULL);
	pthread_join(t2, NULL);
	pthread_barrier_destroy(&ready);
	return 0;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.