[RFC PATCH 1/2] tests/functional: Provide GDB to the functional tests

Thomas Huth posted 2 patches 1 week, 6 days ago
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, "Alex Bennée" <alex.bennee@linaro.org>, Thomas Huth <thuth@redhat.com>, "Marc-André Lureau" <marcandre.lureau@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, Zhao Liu <zhao1.liu@intel.com>
[RFC PATCH 1/2] tests/functional: Provide GDB to the functional tests
Posted by Thomas Huth 1 week, 6 days ago
From: Gustavo Romero <gustavo.romero@linaro.org>

The probe of gdb is done in 'configure' and the full path is passed
to meson.build via the -Dgdb=option.

meson then can pass the location of gdb to the test via an environment
variable.

This patch is based on an earlier patch ("Support tests that require a
runner") by Gustavo Romero.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 configure                     | 2 ++
 meson.build                   | 4 ++++
 meson_options.txt             | 2 ++
 scripts/meson-buildoptions.sh | 2 ++
 tests/functional/meson.build  | 7 +++++++
 5 files changed, 17 insertions(+)

diff --git a/configure b/configure
index 274a7787642..8e2e2cd562a 100755
--- a/configure
+++ b/configure
@@ -1978,6 +1978,8 @@ if test "$skip_meson" = no; then
   test -n "${LIB_FUZZING_ENGINE+xxx}" && meson_option_add "-Dfuzzing_engine=$LIB_FUZZING_ENGINE"
   test "$plugins" = yes && meson_option_add "-Dplugins=true"
   test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg"
+  test -n "$gdb_bin" && meson_option_add "-Dgdb=$gdb_bin"
+
   run_meson() {
     NINJA=$ninja $meson setup "$@" "$PWD" "$source_path"
   }
diff --git a/meson.build b/meson.build
index 3d738733566..4cbc3c8ac65 100644
--- a/meson.build
+++ b/meson.build
@@ -75,6 +75,10 @@ have_user = have_linux_user or have_bsd_user
 
 sh = find_program('sh')
 python = import('python').find_installation()
+# Meson python.get_path() on 'purelib' or 'platlib' doesn't properly return the
+# site-packages dir in pyvenv, so it is built manually.
+python_ver = python.language_version()
+python_site_packages = meson.build_root() / 'pyvenv/lib/python' + python_ver / 'site-packages'
 
 cc = meson.get_compiler('c')
 all_languages = ['c']
diff --git a/meson_options.txt b/meson_options.txt
index fff1521e580..5bb41bcbc43 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -36,6 +36,8 @@ option('trace_file', type: 'string', value: 'trace',
 option('coroutine_backend', type: 'combo',
        choices: ['ucontext', 'sigaltstack', 'windows', 'wasm', 'auto'],
        value: 'auto', description: 'coroutine backend to use')
+option('gdb', type: 'string', value: '',
+       description: 'Path to GDB')
 
 # Everything else can be set via --enable/--disable-* option
 # on the configure script command line.  After adding an option
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 0ebe6bc52a6..f4bd21220ee 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -58,6 +58,7 @@ meson_options_help() {
   printf "%s\n" '  --enable-ubsan           enable undefined behaviour sanitizer'
   printf "%s\n" '  --firmwarepath=VALUES    search PATH for firmware files [share/qemu-'
   printf "%s\n" '                           firmware]'
+  printf "%s\n" '  --gdb=VALUE              Path to GDB'
   printf "%s\n" '  --iasl=VALUE             Path to ACPI disassembler'
   printf "%s\n" '  --includedir=VALUE       Header file directory [include]'
   printf "%s\n" '  --interp-prefix=VALUE    where to find shared libraries etc., use %M for'
@@ -323,6 +324,7 @@ _meson_option_parse() {
     --disable-fuzzing) printf "%s" -Dfuzzing=false ;;
     --enable-gcrypt) printf "%s" -Dgcrypt=enabled ;;
     --disable-gcrypt) printf "%s" -Dgcrypt=disabled ;;
+    --gdb=*) quote_sh "-Dgdb=$2" ;;
     --enable-gettext) printf "%s" -Dgettext=enabled ;;
     --disable-gettext) printf "%s" -Dgettext=disabled ;;
     --enable-gio) printf "%s" -Dgio=enabled ;;
diff --git a/tests/functional/meson.build b/tests/functional/meson.build
index 2a0c5aa1418..c822eb66309 100644
--- a/tests/functional/meson.build
+++ b/tests/functional/meson.build
@@ -77,6 +77,12 @@ foreach speed : ['quick', 'thorough']
     test_env.set('PYTHONPATH', meson.project_source_root() / 'python:' +
                                meson.current_source_dir())
 
+    # Define the GDB environment variable if gdb is available.
+    gdb = get_option('gdb')
+    if gdb != ''
+      test_env.set('QEMU_TEST_GDB', gdb)
+    endif
+
     foreach test : target_tests
       testname = '@0@-@1@'.format(target_base, test)
       if fs.exists('generic' / 'test_' + test + '.py')
@@ -121,6 +127,7 @@ foreach speed : ['quick', 'thorough']
            priority: time_out,
            suite: suites)
     endforeach
+
   endforeach
 endforeach
 
-- 
2.51.0
Re: [RFC PATCH 1/2] tests/functional: Provide GDB to the functional tests
Posted by Gustavo Romero 1 week, 5 days ago
Hi Thomas,

On 9/15/25 09:42, Thomas Huth wrote:
> From: Gustavo Romero <gustavo.romero@linaro.org>
> 
> The probe of gdb is done in 'configure' and the full path is passed
> to meson.build via the -Dgdb=option.
> 
> meson then can pass the location of gdb to the test via an environment
> variable.
> 
> This patch is based on an earlier patch ("Support tests that require a
> runner") by Gustavo Romero.
> 
> Signed-off-by: Thomas Huth <thuth@redhat.com>
> ---
>   configure                     | 2 ++
>   meson.build                   | 4 ++++
>   meson_options.txt             | 2 ++
>   scripts/meson-buildoptions.sh | 2 ++
>   tests/functional/meson.build  | 7 +++++++
>   5 files changed, 17 insertions(+)
> 
> diff --git a/configure b/configure
> index 274a7787642..8e2e2cd562a 100755
> --- a/configure
> +++ b/configure
> @@ -1978,6 +1978,8 @@ if test "$skip_meson" = no; then
>     test -n "${LIB_FUZZING_ENGINE+xxx}" && meson_option_add "-Dfuzzing_engine=$LIB_FUZZING_ENGINE"
>     test "$plugins" = yes && meson_option_add "-Dplugins=true"
>     test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg"
> +  test -n "$gdb_bin" && meson_option_add "-Dgdb=$gdb_bin"
> +
>     run_meson() {
>       NINJA=$ninja $meson setup "$@" "$PWD" "$source_path"
>     }
> diff --git a/meson.build b/meson.build
> index 3d738733566..4cbc3c8ac65 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -75,6 +75,10 @@ have_user = have_linux_user or have_bsd_user
>   
>   sh = find_program('sh')
>   python = import('python').find_installation()
> +# Meson python.get_path() on 'purelib' or 'platlib' doesn't properly return the
> +# site-packages dir in pyvenv, so it is built manually.
> +python_ver = python.language_version()
> +python_site_packages = meson.build_root() / 'pyvenv/lib/python' + python_ver / 'site-packages'
>   
>   cc = meson.get_compiler('c')
>   all_languages = ['c']
> diff --git a/meson_options.txt b/meson_options.txt
> index fff1521e580..5bb41bcbc43 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -36,6 +36,8 @@ option('trace_file', type: 'string', value: 'trace',
>   option('coroutine_backend', type: 'combo',
>          choices: ['ucontext', 'sigaltstack', 'windows', 'wasm', 'auto'],
>          value: 'auto', description: 'coroutine backend to use')
> +option('gdb', type: 'string', value: '',
> +       description: 'Path to GDB')
>   
>   # Everything else can be set via --enable/--disable-* option
>   # on the configure script command line.  After adding an option
> diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
> index 0ebe6bc52a6..f4bd21220ee 100644
> --- a/scripts/meson-buildoptions.sh
> +++ b/scripts/meson-buildoptions.sh
> @@ -58,6 +58,7 @@ meson_options_help() {
>     printf "%s\n" '  --enable-ubsan           enable undefined behaviour sanitizer'
>     printf "%s\n" '  --firmwarepath=VALUES    search PATH for firmware files [share/qemu-'
>     printf "%s\n" '                           firmware]'
> +  printf "%s\n" '  --gdb=VALUE              Path to GDB'
>     printf "%s\n" '  --iasl=VALUE             Path to ACPI disassembler'
>     printf "%s\n" '  --includedir=VALUE       Header file directory [include]'
>     printf "%s\n" '  --interp-prefix=VALUE    where to find shared libraries etc., use %M for'
> @@ -323,6 +324,7 @@ _meson_option_parse() {
>       --disable-fuzzing) printf "%s" -Dfuzzing=false ;;
>       --enable-gcrypt) printf "%s" -Dgcrypt=enabled ;;
>       --disable-gcrypt) printf "%s" -Dgcrypt=disabled ;;
> +    --gdb=*) quote_sh "-Dgdb=$2" ;;
>       --enable-gettext) printf "%s" -Dgettext=enabled ;;
>       --disable-gettext) printf "%s" -Dgettext=disabled ;;
>       --enable-gio) printf "%s" -Dgio=enabled ;;
> diff --git a/tests/functional/meson.build b/tests/functional/meson.build
> index 2a0c5aa1418..c822eb66309 100644
> --- a/tests/functional/meson.build
> +++ b/tests/functional/meson.build
> @@ -77,6 +77,12 @@ foreach speed : ['quick', 'thorough']
>       test_env.set('PYTHONPATH', meson.project_source_root() / 'python:' +
>                                  meson.current_source_dir())

It's necessary to add the Python modules from pyvenv to the PYTHONPATH, otherwise
when libpython from GDB looks for pycotap it cannot find it. We already have
it in python_site_packages in Meson (introduced with this series) so adding
python_site_packages to test_env.set() above, like:

diff --git a/tests/functional/meson.build b/tests/functional/meson.build
index c822eb6630..ce992adb02 100644
--- a/tests/functional/meson.build
+++ b/tests/functional/meson.build
@@ -74,8 +74,9 @@ foreach speed : ['quick', 'thorough']
      endif
      test_env.set('QEMU_TEST_QEMU_BINARY', test_emulator.full_path())
      test_env.set('QEMU_BUILD_ROOT', meson.project_build_root())
-    test_env.set('PYTHONPATH', meson.project_source_root() / 'python:' +
-                               meson.current_source_dir())
+    test_env.set('PYTHONPATH', meson.project_source_root() / 'python' +
+                               ':' + meson.current_source_dir() +
+                               ':' + python_site_packages)
  
      # Define the GDB environment variable if gdb is available.
      gdb = get_option('gdb')


fixes the error:

    Python Exception <class 'ModuleNotFoundError'>: No module named 'pycotap'
   Error occurred in Python: No module named 'pycotap'


>   
> +    # Define the GDB environment variable if gdb is available.
> +    gdb = get_option('gdb')
> +    if gdb != ''
> +      test_env.set('QEMU_TEST_GDB', gdb)
> +    endif
> +
>       foreach test : target_tests
>         testname = '@0@-@1@'.format(target_base, test)
>         if fs.exists('generic' / 'test_' + test + '.py')
> @@ -121,6 +127,7 @@ foreach speed : ['quick', 'thorough']
>              priority: time_out,
>              suite: suites)
>       endforeach
> +

As Alex pointed out, I think there is a stray newline here too.


Cheers,
Gustavo

>     endforeach
>   endforeach
>
Re: [RFC PATCH 1/2] tests/functional: Provide GDB to the functional tests
Posted by Daniel P. Berrangé 1 week, 5 days ago
On Mon, Sep 15, 2025 at 07:02:24PM -0300, Gustavo Romero wrote:
> Hi Thomas,
> 
> On 9/15/25 09:42, Thomas Huth wrote:
> > From: Gustavo Romero <gustavo.romero@linaro.org>
> > 
> > The probe of gdb is done in 'configure' and the full path is passed
> > to meson.build via the -Dgdb=option.
> > 
> > meson then can pass the location of gdb to the test via an environment
> > variable.
> > 
> > This patch is based on an earlier patch ("Support tests that require a
> > runner") by Gustavo Romero.
> > 
> > Signed-off-by: Thomas Huth <thuth@redhat.com>
> > ---
> >   configure                     | 2 ++
> >   meson.build                   | 4 ++++
> >   meson_options.txt             | 2 ++
> >   scripts/meson-buildoptions.sh | 2 ++
> >   tests/functional/meson.build  | 7 +++++++
> >   5 files changed, 17 insertions(+)
> > 
> > diff --git a/configure b/configure
> > index 274a7787642..8e2e2cd562a 100755
> > --- a/configure
> > +++ b/configure
> > @@ -1978,6 +1978,8 @@ if test "$skip_meson" = no; then
> >     test -n "${LIB_FUZZING_ENGINE+xxx}" && meson_option_add "-Dfuzzing_engine=$LIB_FUZZING_ENGINE"
> >     test "$plugins" = yes && meson_option_add "-Dplugins=true"
> >     test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg"
> > +  test -n "$gdb_bin" && meson_option_add "-Dgdb=$gdb_bin"
> > +
> >     run_meson() {
> >       NINJA=$ninja $meson setup "$@" "$PWD" "$source_path"
> >     }
> > diff --git a/meson.build b/meson.build
> > index 3d738733566..4cbc3c8ac65 100644
> > --- a/meson.build
> > +++ b/meson.build
> > @@ -75,6 +75,10 @@ have_user = have_linux_user or have_bsd_user
> >   sh = find_program('sh')
> >   python = import('python').find_installation()
> > +# Meson python.get_path() on 'purelib' or 'platlib' doesn't properly return the
> > +# site-packages dir in pyvenv, so it is built manually.
> > +python_ver = python.language_version()
> > +python_site_packages = meson.build_root() / 'pyvenv/lib/python' + python_ver / 'site-packages'
> >   cc = meson.get_compiler('c')
> >   all_languages = ['c']
> > diff --git a/meson_options.txt b/meson_options.txt
> > index fff1521e580..5bb41bcbc43 100644
> > --- a/meson_options.txt
> > +++ b/meson_options.txt
> > @@ -36,6 +36,8 @@ option('trace_file', type: 'string', value: 'trace',
> >   option('coroutine_backend', type: 'combo',
> >          choices: ['ucontext', 'sigaltstack', 'windows', 'wasm', 'auto'],
> >          value: 'auto', description: 'coroutine backend to use')
> > +option('gdb', type: 'string', value: '',
> > +       description: 'Path to GDB')
> >   # Everything else can be set via --enable/--disable-* option
> >   # on the configure script command line.  After adding an option
> > diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
> > index 0ebe6bc52a6..f4bd21220ee 100644
> > --- a/scripts/meson-buildoptions.sh
> > +++ b/scripts/meson-buildoptions.sh
> > @@ -58,6 +58,7 @@ meson_options_help() {
> >     printf "%s\n" '  --enable-ubsan           enable undefined behaviour sanitizer'
> >     printf "%s\n" '  --firmwarepath=VALUES    search PATH for firmware files [share/qemu-'
> >     printf "%s\n" '                           firmware]'
> > +  printf "%s\n" '  --gdb=VALUE              Path to GDB'
> >     printf "%s\n" '  --iasl=VALUE             Path to ACPI disassembler'
> >     printf "%s\n" '  --includedir=VALUE       Header file directory [include]'
> >     printf "%s\n" '  --interp-prefix=VALUE    where to find shared libraries etc., use %M for'
> > @@ -323,6 +324,7 @@ _meson_option_parse() {
> >       --disable-fuzzing) printf "%s" -Dfuzzing=false ;;
> >       --enable-gcrypt) printf "%s" -Dgcrypt=enabled ;;
> >       --disable-gcrypt) printf "%s" -Dgcrypt=disabled ;;
> > +    --gdb=*) quote_sh "-Dgdb=$2" ;;
> >       --enable-gettext) printf "%s" -Dgettext=enabled ;;
> >       --disable-gettext) printf "%s" -Dgettext=disabled ;;
> >       --enable-gio) printf "%s" -Dgio=enabled ;;
> > diff --git a/tests/functional/meson.build b/tests/functional/meson.build
> > index 2a0c5aa1418..c822eb66309 100644
> > --- a/tests/functional/meson.build
> > +++ b/tests/functional/meson.build
> > @@ -77,6 +77,12 @@ foreach speed : ['quick', 'thorough']
> >       test_env.set('PYTHONPATH', meson.project_source_root() / 'python:' +
> >                                  meson.current_source_dir())
> 
> It's necessary to add the Python modules from pyvenv to the PYTHONPATH, otherwise
> when libpython from GDB looks for pycotap it cannot find it. We already have
> it in python_site_packages in Meson (introduced with this series) so adding
> python_site_packages to test_env.set() above, like:

This dovetails to the point I made on the cover letter.

The pyvenv is populated wrt the python version we selected for QEMU.
This cannot be assumed to be the same as the version that GDB was
built against. Pointing GDB to a venv for a different python version
is not a good idea.


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
Re: [RFC PATCH 1/2] tests/functional: Provide GDB to the functional tests
Posted by Alex Bennée 1 week, 6 days ago
Thomas Huth <thuth@redhat.com> writes:

> From: Gustavo Romero <gustavo.romero@linaro.org>
>
> The probe of gdb is done in 'configure' and the full path is passed
> to meson.build via the -Dgdb=option.
>
> meson then can pass the location of gdb to the test via an environment
> variable.
>
> This patch is based on an earlier patch ("Support tests that require a
> runner") by Gustavo Romero.
>
> Signed-off-by: Thomas Huth <thuth@redhat.com>
> ---
<snip>
>      foreach test : target_tests
>        testname = '@0@-@1@'.format(target_base, test)
>        if fs.exists('generic' / 'test_' + test + '.py')
> @@ -121,6 +127,7 @@ foreach speed : ['quick', 'thorough']
>             priority: time_out,
>             suite: suites)
>      endforeach
> +

spare newline?

>    endforeach
>  endforeach

Otherwise:

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro