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;
}