bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH 9/9] Add a test for thread state


From: Samuel Thibault
Subject: Re: [PATCH 9/9] Add a test for thread state
Date: Tue, 16 Apr 2024 15:39:41 +0200
User-agent: NeoMutt/20170609 (1.8.3)

Sergey Bugaev, le mar. 16 avril 2024 10:10:11 +0300, a ecrit:
> On Tue, Apr 16, 2024 at 4:01 AM Samuel Thibault <samuel.thibault@gnu.org> 
> wrote:
> > Ah, no, I mis read the result. It does stay stuck on x86_64.
> 
> Indeed, thanks. Reproduced and fixed; I was accidentally using rsp
> (instead of ursp) in one place.

Applied, thanks!

> This tests generating and handling exceptions, thread_get_state(),
> thread_set_state(), and newly added thread_set_self_state().  It does
> many of the same things that glibc does when handling a signal.
> ---
>  tests/test-thread-state.c | 215 ++++++++++++++++++++++++++++++++++++++
>  tests/user-qemu.mk        |   3 +-
>  2 files changed, 217 insertions(+), 1 deletion(-)
>  create mode 100644 tests/test-thread-state.c
> 
> diff --git a/tests/test-thread-state.c b/tests/test-thread-state.c
> new file mode 100644
> index 00000000..b78ab110
> --- /dev/null
> +++ b/tests/test-thread-state.c
> @@ -0,0 +1,215 @@
> +/*
> + * Copyright (c) 2024 Free Software Foundation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + */
> +
> +#include <syscalls.h>
> +#include <testlib.h>
> +#include <mach/exception.h>
> +#include <mach.user.h>
> +#include <mach_port.user.h>
> +
> +#if defined(__x86_64__) || defined(__i386__)
> +#define THREAD_STATE_FLAVOR  i386_THREAD_STATE
> +#define THREAD_STATE_COUNT   i386_THREAD_STATE_COUNT
> +#elif defined(__aarch64__)
> +#define THREAD_STATE_FLAVOR  AARCH64_THREAD_STATE
> +#define THREAD_STATE_COUNT   AARCH64_THREAD_STATE_COUNT
> +#else
> +#error "Don't know which state to use on this platform"
> +#endif
> +
> +/*
> + *   We'll make the thread itself run this function when it faults.
> + *   This simulates handling a Unix SIGSEGV.
> + */
> +static void __attribute__((noreturn)) fault_handler(
> +     vm_offset_t     fault_address,
> +     thread_state_t  state)
> +{
> +     kern_return_t   kr;
> +     vm_size_t       i;
> +     vm_offset_t     addr = fault_address;
> +
> +     printf("Handling a fault at 0x%p\n", fault_address);
> +
> +     /*
> +      *      Allocate the missing area of memory.
> +      */
> +     kr = vm_allocate(mach_task_self(), &addr, vm_page_size, FALSE);
> +     ASSERT_RET(kr, "failed to allocate missing memory");
> +
> +     /*
> +      *      Fill it with some data.
> +      */
> +     for (i = 0; i < vm_page_size / sizeof(int); i++)
> +             *((int *) addr + i) = i;
> +
> +     /*
> +      *      Return back to the interrupted code.
> +      */
> +     kr = thread_set_self_state(THREAD_STATE_FLAVOR, state,
> +                                THREAD_STATE_COUNT);
> +     ASSERT_RET(kr, "thread_set_self_state failed");
> +     FAILURE("thread_set_self_state returned");
> +}
> +
> +/*
> + *   The exception_raise() RPC handler.  Mach calls this when the other 
> thread faults.
> + *   This runs in a different thread; it could fix things up directly and 
> resume the
> + *   thread.  Instead, it sets things up so that the thread itself will fix 
> things
> + *   for itself, and then return back to what it was doing.
> + */
> +kern_return_t catch_exception_raise(
> +     mach_port_t     exception_port,
> +     thread_t        thread,
> +     task_t          task,
> +     integer_t       exception,
> +     integer_t       code,
> +     long_integer_t  subcode)
> +{
> +     kern_return_t                   kr;
> +     vm_offset_t                     off;
> +#if defined(__x86_64__) || defined(__i386__)
> +     struct i386_thread_state        state;
> +#elif defined(__aarch64__)
> +     struct aarch64_thread_state     state;
> +#else
> +#error "Don't know which state to use on this platform"
> +#endif
> +     mach_msg_type_number_t          state_count = THREAD_STATE_COUNT;
> +
> +
> +     printf("Received exception_raise(%u %u 0x%lx)\n", exception, code, 
> subcode);
> +
> +     /*
> +      *      We only want to handle EXC_BAD_ACCESS/KERN_INVALID_ADDRESS.
> +      *      Return an error to proceed with the default handling otherwise.
> +      */
> +     if (exception != EXC_BAD_ACCESS)
> +             return KERN_FAILURE;
> +     if (code != KERN_INVALID_ADDRESS)
> +             return KERN_FAILURE;
> +
> +     kr = thread_get_state(thread, THREAD_STATE_FLAVOR,
> +                           (thread_state_t) &state, &state_count);
> +     ASSERT_RET(kr, "thread_get_state get failed");
> +     ASSERT(state_count == THREAD_STATE_COUNT, "bad state_count");
> +
> +#if defined(__x86_64__)
> +     /*
> +      *      Place a copy of the state on the thread's stack.
> +      */
> +     off = ((state.ursp - 128 - sizeof(state)) & ~15UL) - 8;
> +     memcpy((void *) off, &state, sizeof(state));
> +
> +     /*
> +      *      Make it call fault_handler(subcode, off).
> +      */
> +     state.ursp = off;
> +     state.rip = (vm_offset_t) fault_handler;
> +     state.rdi = (vm_offset_t) subcode;
> +     state.rsi = off;
> +#elif defined(__i386__)
> +     /*
> +      *      Place a copy of the state on the thread's stack.
> +      */
> +     off = state.uesp - sizeof(state);
> +     memcpy((void *) off, &state, sizeof(state));
> +
> +     /*
> +      *      Make it call fault_handler(subcode, off).
> +      */
> +     *(vm_offset_t *) (off - 4) = off;
> +     *(vm_offset_t *) (off - 8) = (vm_offset_t) subcode;
> +     state.uesp = off - 12;
> +     state.eip = (vm_offset_t) fault_handler;
> +#elif defined(__aarch64__)
> +     /*
> +      *      Place a copy of the state on the thread's stack.
> +      */
> +     off = (state.sp - sizeof(state)) & ~15UL;
> +     memcpy((void *) off, &state, sizeof(state));
> +
> +     /*
> +      *      Make it call fault_handler(subcode, off).
> +      */
> +     state.sp = off;
> +     state.pc = (vm_offset_t) fault_handler;
> +     state.x[0] = (vm_offset_t) subcode;
> +     state.x[1] = off;
> +#else
> +#error "Don't know how to manipulate state to use on this platform"
> +#endif
> +
> +     kr = thread_set_state(thread, THREAD_STATE_FLAVOR,
> +                           (thread_state_t) &state, state_count);
> +     ASSERT_RET(kr, "thread_set_state failed");
> +
> +     /*
> +      *      Our job here is done!  Returning success resumes the thread.
> +      */
> +     mach_port_deallocate(mach_task_self(), thread);
> +     mach_port_deallocate(mach_task_self(), task);
> +
> +     return KERN_SUCCESS;
> +}
> +
> +static void exc_server_thread_body(void *arg)
> +{
> +     kern_return_t   kr;
> +     mach_port_t     exc_port = (mach_port_t) (vm_offset_t) arg;
> +
> +     boolean_t exc_server(
> +             mach_msg_header_t       *request,
> +             mach_msg_header_t       *reply);
> +
> +     kr = mach_msg_server(exc_server, 4096, exc_port, MACH_MSG_OPTION_NONE);
> +     ASSERT_RET(kr, "error in mach_msg_server");
> +}
> +
> +static void do_count(void)
> +{
> +     const int       *arr = (const int *) 0x10000000;
> +     int             i;
> +     unsigned long   count = 0;
> +
> +     for (i = 0; i < vm_page_size / sizeof(int) * 3; i++)
> +             count += arr[i];
> +
> +     ASSERT(vm_page_size == 4096, "need a different answer for a different 
> page size");
> +     ASSERT(count == 0x17fa00, "bad count");
> +}
> +
> +int main(int argc, char *argv[], int envc, char *envp[])
> +{
> +     kern_return_t   kr;
> +     mach_port_t     exc_port = mach_reply_port();
> +
> +     test_thread_start(mach_task_self(), exc_server_thread_body,
> +                       (void *) (vm_offset_t) exc_port);
> +
> +     kr = mach_port_insert_right(mach_task_self(), exc_port,
> +                                 exc_port, MACH_MSG_TYPE_MAKE_SEND);
> +     ASSERT_RET(kr, "mach_port_insert_right");
> +
> +     kr = thread_set_exception_port(mach_thread_self(), exc_port);
> +     ASSERT_RET(kr, "thread_set_exception_port failed");
> +
> +     do_count();
> +
> +     return 0;
> +}
> diff --git a/tests/user-qemu.mk b/tests/user-qemu.mk
> index 3b546252..a3013b59 100644
> --- a/tests/user-qemu.mk
> +++ b/tests/user-qemu.mk
> @@ -209,7 +209,8 @@ USER_TESTS := \
>       tests/test-syscalls \
>       tests/test-machmsg \
>       tests/test-task \
> -     tests/test-threads
> +     tests/test-threads \
> +     tests/test-thread-state
>  
>  USER_TESTS_CLEAN = $(subst tests/,clean-,$(USER_TESTS))
>  
> -- 
> 2.44.0
> 

-- 
Samuel
---
Pour une évaluation indépendante, transparente et rigoureuse !
Je soutiens la Commission d'Évaluation de l'Inria.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]