diff mbox series

[v4,04/15] qemu-iotests: add option to attach gdbserver

Message ID 20210520075236.44723-5-eesposit@redhat.com (mailing list archive)
State New, archived
Headers show
Series qemu_iotests: improve debugging options | expand

Commit Message

Emanuele Giuseppe Esposito May 20, 2021, 7:52 a.m. UTC
Define -gdb flag and GDB_OPTIONS environment variable
to python tests to attach a gdbserver to each qemu instance.
This patch only adds and parses this flag, it does not yet add
the implementation for it.

if -gdb is not provided but $GDB_OPTIONS is set, ignore the
environment variable.

Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
---
 tests/qemu-iotests/check      |  6 +++++-
 tests/qemu-iotests/iotests.py |  5 +++++
 tests/qemu-iotests/testenv.py | 19 ++++++++++++++++---
 3 files changed, 26 insertions(+), 4 deletions(-)

Comments

Vladimir Sementsov-Ogievskiy May 26, 2021, 11:24 a.m. UTC | #1
20.05.2021 10:52, Emanuele Giuseppe Esposito wrote:
> Define -gdb flag and GDB_OPTIONS environment variable

Let's use --option notation for new long options

> to python tests to attach a gdbserver to each qemu instance.
> This patch only adds and parses this flag, it does not yet add
> the implementation for it.
> 
> if -gdb is not provided but $GDB_OPTIONS is set, ignore the
> environment variable.
> 
> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
> ---
>   tests/qemu-iotests/check      |  6 +++++-
>   tests/qemu-iotests/iotests.py |  5 +++++
>   tests/qemu-iotests/testenv.py | 19 ++++++++++++++++---
>   3 files changed, 26 insertions(+), 4 deletions(-)
> 
> diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
> index d1c87ceaf1..b9820fdaaf 100755
> --- a/tests/qemu-iotests/check
> +++ b/tests/qemu-iotests/check
> @@ -33,6 +33,9 @@ def make_argparser() -> argparse.ArgumentParser:
>                      help='pretty print output for make check')
>   
>       p.add_argument('-d', dest='debug', action='store_true', help='debug')
> +    p.add_argument('-gdb', action='store_true',
> +                   help="start gdbserver with $GDB_OPTIONS options \
> +                        ('localhost:12345' if $GDB_OPTIONS is empty)")

Hmm.. Why not just make --gdb a string option, that defaults to GDB_OPTIONS? This way it will more similar with other options.

>       p.add_argument('-misalign', action='store_true',
>                      help='misalign memory allocations')
>       p.add_argument('--color', choices=['on', 'off', 'auto'],
> @@ -112,7 +115,8 @@ if __name__ == '__main__':
>       env = TestEnv(imgfmt=args.imgfmt, imgproto=args.imgproto,
>                     aiomode=args.aiomode, cachemode=args.cachemode,
>                     imgopts=args.imgopts, misalign=args.misalign,
> -                  debug=args.debug, valgrind=args.valgrind)
> +                  debug=args.debug, valgrind=args.valgrind,
> +                  gdb=args.gdb)
>   
>       testfinder = TestFinder(test_dir=env.source_iotests)
>   
> diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
> index 5d78de0f0b..d667fde6f8 100644
> --- a/tests/qemu-iotests/iotests.py
> +++ b/tests/qemu-iotests/iotests.py
> @@ -75,6 +75,11 @@
>   qemu_prog = os.environ.get('QEMU_PROG', 'qemu')
>   qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ')
>   
> +gdb_qemu_env = os.environ.get('GDB_OPTIONS')
> +qemu_gdb = []
> +if gdb_qemu_env:
> +    qemu_gdb = ['gdbserver'] + gdb_qemu_env.strip().split(' ')
> +
>   imgfmt = os.environ.get('IMGFMT', 'raw')
>   imgproto = os.environ.get('IMGPROTO', 'file')
>   output_dir = os.environ.get('OUTPUT_DIR', '.')
> diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py
> index 6d27712617..49ddd586ef 100644
> --- a/tests/qemu-iotests/testenv.py
> +++ b/tests/qemu-iotests/testenv.py
> @@ -27,6 +27,7 @@
>   import glob
>   from typing import Dict, Any, Optional, ContextManager
>   
> +DEF_GDB_OPTIONS = 'localhost:12345'
>   
>   def isxfile(path: str) -> bool:
>       return os.path.isfile(path) and os.access(path, os.X_OK)
> @@ -72,7 +73,8 @@ class TestEnv(ContextManager['TestEnv']):
>                        'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 'IMGPROTO',
>                        'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU',
>                        'CACHEMODE_IS_DEFAULT', 'IMGFMT_GENERIC', 'IMGOPTSSYNTAX',
> -                     'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 'MALLOC_PERTURB_']
> +                     'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 'MALLOC_PERTURB_',
> +                     'GDB_OPTIONS']
>   
>       def get_env(self) -> Dict[str, str]:
>           env = {}
> @@ -163,7 +165,8 @@ def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
>                    imgopts: Optional[str] = None,
>                    misalign: bool = False,
>                    debug: bool = False,
> -                 valgrind: bool = False) -> None:
> +                 valgrind: bool = False,
> +                 gdb: bool = False) -> None:
>           self.imgfmt = imgfmt
>           self.imgproto = imgproto
>           self.aiomode = aiomode
> @@ -171,6 +174,14 @@ def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
>           self.misalign = misalign
>           self.debug = debug
>   
> +        if gdb:
> +            self.gdb_options = os.environ.get('GDB_OPTIONS', DEF_GDB_OPTIONS)
> +            if not self.gdb_options:
> +                # cover the case 'export GDB_OPTIONS='
> +                self.gdb_options = DEF_GDB_OPTIONS
> +        elif 'GDB_OPTIONS' in os.environ:
> +            del os.environ['GDB_OPTIONS']
> +
>           if valgrind:
>               self.valgrind_qemu = 'y'
>   
> @@ -269,7 +280,9 @@ def print_env(self) -> None:
>   PLATFORM      -- {platform}
>   TEST_DIR      -- {TEST_DIR}
>   SOCK_DIR      -- {SOCK_DIR}
> -SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}"""
> +SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}
> +GDB_OPTIONS   -- {GDB_OPTIONS}
> +"""
>   
>           args = collections.defaultdict(str, self.get_env())
>   
>
Paolo Bonzini May 26, 2021, 12:48 p.m. UTC | #2
On 26/05/21 13:24, Vladimir Sementsov-Ogievskiy wrote:
> 
>> Define -gdb flag and GDB_OPTIONS environment variable
> 
> Let's use --option notation for new long options

Why make a mix of two styles? -- suggests that single-character options 
like -d and -v can be combined, is that the case?

>> if -gdb is not provided but $GDB_OPTIONS is set, ignore the
>> environment variable.
>>
>> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
>> ---
>>   tests/qemu-iotests/check      |  6 +++++-
>>   tests/qemu-iotests/iotests.py |  5 +++++
>>   tests/qemu-iotests/testenv.py | 19 ++++++++++++++++---
>>   3 files changed, 26 insertions(+), 4 deletions(-)
>>
>> diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
>> index d1c87ceaf1..b9820fdaaf 100755
>> --- a/tests/qemu-iotests/check
>> +++ b/tests/qemu-iotests/check
>> @@ -33,6 +33,9 @@ def make_argparser() -> argparse.ArgumentParser:
>>                      help='pretty print output for make check')
>>       p.add_argument('-d', dest='debug', action='store_true', 
>> help='debug')
>> +    p.add_argument('-gdb', action='store_true',
>> +                   help="start gdbserver with $GDB_OPTIONS options \
>> +                        ('localhost:12345' if $GDB_OPTIONS is empty)")
> 
> Hmm.. Why not just make --gdb a string option, that defaults to 
> GDB_OPTIONS? This way it will more similar with other options.

I think then something like "./check -gdb 030" would not work, right?

Paolo
Vladimir Sementsov-Ogievskiy May 26, 2021, 1:25 p.m. UTC | #3
26.05.2021 15:48, Paolo Bonzini wrote:
> On 26/05/21 13:24, Vladimir Sementsov-Ogievskiy wrote:
>>
>>> Define -gdb flag and GDB_OPTIONS environment variable
>>
>> Let's use --option notation for new long options
> 
> Why make a mix of two styles? -- suggests that single-character options like -d and -v can be combined, is that the case?

Yes.. I think think that --options (with -o short options) is more usual and modern style.

We already have both --options and -options.. So, my idea when I was rewriting ./check was that better to move to --options. I can send patch to change all existing -options of check to be --options for full consistency. It would be some pain for developers..

> 
>>> if -gdb is not provided but $GDB_OPTIONS is set, ignore the
>>> environment variable.
>>>
>>> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
>>> ---
>>>   tests/qemu-iotests/check      |  6 +++++-
>>>   tests/qemu-iotests/iotests.py |  5 +++++
>>>   tests/qemu-iotests/testenv.py | 19 ++++++++++++++++---
>>>   3 files changed, 26 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
>>> index d1c87ceaf1..b9820fdaaf 100755
>>> --- a/tests/qemu-iotests/check
>>> +++ b/tests/qemu-iotests/check
>>> @@ -33,6 +33,9 @@ def make_argparser() -> argparse.ArgumentParser:
>>>                      help='pretty print output for make check')
>>>       p.add_argument('-d', dest='debug', action='store_true', help='debug')
>>> +    p.add_argument('-gdb', action='store_true',
>>> +                   help="start gdbserver with $GDB_OPTIONS options \
>>> +                        ('localhost:12345' if $GDB_OPTIONS is empty)")
>>
>> Hmm.. Why not just make --gdb a string option, that defaults to GDB_OPTIONS? This way it will more similar with other options.
> 
> I think then something like "./check -gdb 030" would not work, right?
> 

Hmm, yes, that's not very convenient. OK then, let's keep bool.
Emanuele Giuseppe Esposito May 26, 2021, 5:23 p.m. UTC | #4
On 26/05/2021 15:25, Vladimir Sementsov-Ogievskiy wrote:
> 26.05.2021 15:48, Paolo Bonzini wrote:
>> On 26/05/21 13:24, Vladimir Sementsov-Ogievskiy wrote:
>>>
>>>> Define -gdb flag and GDB_OPTIONS environment variable
>>>
>>> Let's use --option notation for new long options
>>
>> Why make a mix of two styles? -- suggests that single-character 
>> options like -d and -v can be combined, is that the case?
> 
> Yes.. I think think that --options (with -o short options) is more usual 
> and modern style.
> 
> We already have both --options and -options.. So, my idea when I was 
> rewriting ./check was that better to move to --options. I can send patch 
> to change all existing -options of check to be --options for full 
> consistency. It would be some pain for developers..

I am following the current convention. I put gdb on the same 
level/category of valgrind, and since the current option is -valgrind, I 
would like to stick to that. If you want to send a patch changing all 
-options in --options, feel free to do it in a separate series that 
changes also -gdb :)

Thank you,
Emanuele
> 
>>
>>>> if -gdb is not provided but $GDB_OPTIONS is set, ignore the
>>>> environment variable.
>>>>
>>>> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
>>>> ---
>>>>   tests/qemu-iotests/check      |  6 +++++-
>>>>   tests/qemu-iotests/iotests.py |  5 +++++
>>>>   tests/qemu-iotests/testenv.py | 19 ++++++++++++++++---
>>>>   3 files changed, 26 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
>>>> index d1c87ceaf1..b9820fdaaf 100755
>>>> --- a/tests/qemu-iotests/check
>>>> +++ b/tests/qemu-iotests/check
>>>> @@ -33,6 +33,9 @@ def make_argparser() -> argparse.ArgumentParser:
>>>>                      help='pretty print output for make check')
>>>>       p.add_argument('-d', dest='debug', action='store_true', 
>>>> help='debug')
>>>> +    p.add_argument('-gdb', action='store_true',
>>>> +                   help="start gdbserver with $GDB_OPTIONS options \
>>>> +                        ('localhost:12345' if $GDB_OPTIONS is empty)")
>>>
>>> Hmm.. Why not just make --gdb a string option, that defaults to 
>>> GDB_OPTIONS? This way it will more similar with other options.
>>
>> I think then something like "./check -gdb 030" would not work, right?
>>
> 
> Hmm, yes, that's not very convenient. OK then, let's keep bool.
> 
>
Vladimir Sementsov-Ogievskiy May 26, 2021, 7:15 p.m. UTC | #5
20.05.2021 10:52, Emanuele Giuseppe Esposito wrote:
> Define -gdb flag and GDB_OPTIONS environment variable
> to python tests to attach a gdbserver to each qemu instance.
> This patch only adds and parses this flag, it does not yet add
> the implementation for it.
> 
> if -gdb is not provided but $GDB_OPTIONS is set, ignore the
> environment variable.
> 
> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
> ---
>   tests/qemu-iotests/check      |  6 +++++-
>   tests/qemu-iotests/iotests.py |  5 +++++
>   tests/qemu-iotests/testenv.py | 19 ++++++++++++++++---
>   3 files changed, 26 insertions(+), 4 deletions(-)
> 
> diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
> index d1c87ceaf1..b9820fdaaf 100755
> --- a/tests/qemu-iotests/check
> +++ b/tests/qemu-iotests/check
> @@ -33,6 +33,9 @@ def make_argparser() -> argparse.ArgumentParser:
>                      help='pretty print output for make check')
>   
>       p.add_argument('-d', dest='debug', action='store_true', help='debug')
> +    p.add_argument('-gdb', action='store_true',
> +                   help="start gdbserver with $GDB_OPTIONS options \
> +                        ('localhost:12345' if $GDB_OPTIONS is empty)")
>       p.add_argument('-misalign', action='store_true',
>                      help='misalign memory allocations')
>       p.add_argument('--color', choices=['on', 'off', 'auto'],
> @@ -112,7 +115,8 @@ if __name__ == '__main__':
>       env = TestEnv(imgfmt=args.imgfmt, imgproto=args.imgproto,
>                     aiomode=args.aiomode, cachemode=args.cachemode,
>                     imgopts=args.imgopts, misalign=args.misalign,
> -                  debug=args.debug, valgrind=args.valgrind)
> +                  debug=args.debug, valgrind=args.valgrind,
> +                  gdb=args.gdb)
>   
>       testfinder = TestFinder(test_dir=env.source_iotests)
>   
> diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
> index 5d78de0f0b..d667fde6f8 100644
> --- a/tests/qemu-iotests/iotests.py
> +++ b/tests/qemu-iotests/iotests.py
> @@ -75,6 +75,11 @@
>   qemu_prog = os.environ.get('QEMU_PROG', 'qemu')
>   qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ')
>   
> +gdb_qemu_env = os.environ.get('GDB_OPTIONS')

should we specify a default? otherwise get() should raise an exception when GDB_OPTIONS is not set..

or you need os.getenv, which will return None if variable is absent.

> +qemu_gdb = []
> +if gdb_qemu_env:
> +    qemu_gdb = ['gdbserver'] + gdb_qemu_env.strip().split(' ')
> +
>   imgfmt = os.environ.get('IMGFMT', 'raw')
>   imgproto = os.environ.get('IMGPROTO', 'file')
>   output_dir = os.environ.get('OUTPUT_DIR', '.')
> diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py
> index 6d27712617..49ddd586ef 100644
> --- a/tests/qemu-iotests/testenv.py
> +++ b/tests/qemu-iotests/testenv.py
> @@ -27,6 +27,7 @@
>   import glob
>   from typing import Dict, Any, Optional, ContextManager
>   
> +DEF_GDB_OPTIONS = 'localhost:12345'
>   
>   def isxfile(path: str) -> bool:
>       return os.path.isfile(path) and os.access(path, os.X_OK)
> @@ -72,7 +73,8 @@ class TestEnv(ContextManager['TestEnv']):
>                        'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 'IMGPROTO',
>                        'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU',
>                        'CACHEMODE_IS_DEFAULT', 'IMGFMT_GENERIC', 'IMGOPTSSYNTAX',
> -                     'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 'MALLOC_PERTURB_']
> +                     'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 'MALLOC_PERTURB_',
> +                     'GDB_OPTIONS']
>   
>       def get_env(self) -> Dict[str, str]:
>           env = {}
> @@ -163,7 +165,8 @@ def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
>                    imgopts: Optional[str] = None,
>                    misalign: bool = False,
>                    debug: bool = False,
> -                 valgrind: bool = False) -> None:
> +                 valgrind: bool = False,
> +                 gdb: bool = False) -> None:
>           self.imgfmt = imgfmt
>           self.imgproto = imgproto
>           self.aiomode = aiomode
> @@ -171,6 +174,14 @@ def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
>           self.misalign = misalign
>           self.debug = debug
>   
> +        if gdb:
> +            self.gdb_options = os.environ.get('GDB_OPTIONS', DEF_GDB_OPTIONS)

everywhere in the file os.getenv is used, let's be consistent on it.

> +            if not self.gdb_options:
> +                # cover the case 'export GDB_OPTIONS='
> +                self.gdb_options = DEF_GDB_OPTIONS

Hmm, a bit strange to handle 'export X=' only for this new variable, we don't have such logic for other variables.. I'm not against still, if you need it.

> +        elif 'GDB_OPTIONS' in os.environ:
> +            del os.environ['GDB_OPTIONS']

Don't need to remove variable from envirton, it has no effect. The task of TestEnv class is to prepare environment in its attributes, listed in env_variables. Then prepared attributes are available through get_env(). So if you don't want to provide GDB_OPTIONS, it's enough to not initialize self.gdb_options. So, just drop "elif:" branch.

> +
>           if valgrind:
>               self.valgrind_qemu = 'y'
>   
> @@ -269,7 +280,9 @@ def print_env(self) -> None:
>   PLATFORM      -- {platform}
>   TEST_DIR      -- {TEST_DIR}
>   SOCK_DIR      -- {SOCK_DIR}
> -SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}"""
> +SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}
> +GDB_OPTIONS   -- {GDB_OPTIONS}

Not sure we need to see this additional line every time.. Maybe, show it only when gdb is set?

> +"""
>   
>           args = collections.defaultdict(str, self.get_env())
>   
>
Emanuele Giuseppe Esposito May 27, 2021, 11:06 a.m. UTC | #6
On 26/05/2021 21:15, Vladimir Sementsov-Ogievskiy wrote:
> 20.05.2021 10:52, Emanuele Giuseppe Esposito wrote:
>> Define -gdb flag and GDB_OPTIONS environment variable
>> to python tests to attach a gdbserver to each qemu instance.
>> This patch only adds and parses this flag, it does not yet add
>> the implementation for it.
>>
>> if -gdb is not provided but $GDB_OPTIONS is set, ignore the
>> environment variable.
>>
>> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
>> ---
>>   tests/qemu-iotests/check      |  6 +++++-
>>   tests/qemu-iotests/iotests.py |  5 +++++
>>   tests/qemu-iotests/testenv.py | 19 ++++++++++++++++---
>>   3 files changed, 26 insertions(+), 4 deletions(-)
>>
>> diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
>> index d1c87ceaf1..b9820fdaaf 100755
>> --- a/tests/qemu-iotests/check
>> +++ b/tests/qemu-iotests/check
>> @@ -33,6 +33,9 @@ def make_argparser() -> argparse.ArgumentParser:
>>                      help='pretty print output for make check')
>>       p.add_argument('-d', dest='debug', action='store_true', 
>> help='debug')
>> +    p.add_argument('-gdb', action='store_true',
>> +                   help="start gdbserver with $GDB_OPTIONS options \
>> +                        ('localhost:12345' if $GDB_OPTIONS is empty)")
>>       p.add_argument('-misalign', action='store_true',
>>                      help='misalign memory allocations')
>>       p.add_argument('--color', choices=['on', 'off', 'auto'],
>> @@ -112,7 +115,8 @@ if __name__ == '__main__':
>>       env = TestEnv(imgfmt=args.imgfmt, imgproto=args.imgproto,
>>                     aiomode=args.aiomode, cachemode=args.cachemode,
>>                     imgopts=args.imgopts, misalign=args.misalign,
>> -                  debug=args.debug, valgrind=args.valgrind)
>> +                  debug=args.debug, valgrind=args.valgrind,
>> +                  gdb=args.gdb)
>>       testfinder = TestFinder(test_dir=env.source_iotests)
>> diff --git a/tests/qemu-iotests/iotests.py 
>> b/tests/qemu-iotests/iotests.py
>> index 5d78de0f0b..d667fde6f8 100644
>> --- a/tests/qemu-iotests/iotests.py
>> +++ b/tests/qemu-iotests/iotests.py
>> @@ -75,6 +75,11 @@
>>   qemu_prog = os.environ.get('QEMU_PROG', 'qemu')
>>   qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ')
>> +gdb_qemu_env = os.environ.get('GDB_OPTIONS')
> 
> should we specify a default? otherwise get() should raise an exception 
> when GDB_OPTIONS is not set..
> 
> or you need os.getenv, which will return None if variable is absent.

No, os.environ.get does not raise any exception. I tested it myself, plus:
https://stackoverflow.com/questions/16924471/difference-between-os-getenv-and-os-environ-get

> 
>> +qemu_gdb = []
>> +if gdb_qemu_env:
>> +    qemu_gdb = ['gdbserver'] + gdb_qemu_env.strip().split(' ')
>> +
>>   imgfmt = os.environ.get('IMGFMT', 'raw')
>>   imgproto = os.environ.get('IMGPROTO', 'file')
>>   output_dir = os.environ.get('OUTPUT_DIR', '.')
>> diff --git a/tests/qemu-iotests/testenv.py 
>> b/tests/qemu-iotests/testenv.py
>> index 6d27712617..49ddd586ef 100644
>> --- a/tests/qemu-iotests/testenv.py
>> +++ b/tests/qemu-iotests/testenv.py
>> @@ -27,6 +27,7 @@
>>   import glob
>>   from typing import Dict, Any, Optional, ContextManager
>> +DEF_GDB_OPTIONS = 'localhost:12345'
>>   def isxfile(path: str) -> bool:
>>       return os.path.isfile(path) and os.access(path, os.X_OK)
>> @@ -72,7 +73,8 @@ class TestEnv(ContextManager['TestEnv']):
>>                        'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 
>> 'IMGPROTO',
>>                        'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU',
>>                        'CACHEMODE_IS_DEFAULT', 'IMGFMT_GENERIC', 
>> 'IMGOPTSSYNTAX',
>> -                     'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 
>> 'MALLOC_PERTURB_']
>> +                     'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 
>> 'MALLOC_PERTURB_',
>> +                     'GDB_OPTIONS']
>>       def get_env(self) -> Dict[str, str]:
>>           env = {}
>> @@ -163,7 +165,8 @@ def __init__(self, imgfmt: str, imgproto: str, 
>> aiomode: str,
>>                    imgopts: Optional[str] = None,
>>                    misalign: bool = False,
>>                    debug: bool = False,
>> -                 valgrind: bool = False) -> None:
>> +                 valgrind: bool = False,
>> +                 gdb: bool = False) -> None:
>>           self.imgfmt = imgfmt
>>           self.imgproto = imgproto
>>           self.aiomode = aiomode
>> @@ -171,6 +174,14 @@ def __init__(self, imgfmt: str, imgproto: str, 
>> aiomode: str,
>>           self.misalign = misalign
>>           self.debug = debug
>> +        if gdb:
>> +            self.gdb_options = os.environ.get('GDB_OPTIONS', 
>> DEF_GDB_OPTIONS)
> 
> everywhere in the file os.getenv is used, let's be consistent on it.
> 
>> +            if not self.gdb_options:
>> +                # cover the case 'export GDB_OPTIONS='
>> +                self.gdb_options = DEF_GDB_OPTIONS
> 
> Hmm, a bit strange to handle 'export X=' only for this new variable, we 
> don't have such logic for other variables.. I'm not against still, if 
> you need it.
> 
>> +        elif 'GDB_OPTIONS' in os.environ:
>> +            del os.environ['GDB_OPTIONS']
> 
> Don't need to remove variable from envirton, it has no effect. The task 
> of TestEnv class is to prepare environment in its attributes, listed in 
> env_variables. Then prepared attributes are available through get_env(). 
> So if you don't want to provide GDB_OPTIONS, it's enough to not 
> initialize self.gdb_options. So, just drop "elif:" branch.

You forget that there are bash tests :)
Think about the following case:

# export GDB_OPTIONS="localhost:1236"

# ./check -qcow2 007 # a script test

The output will contain:
GDB_OPTIONS   --
VALGRIND_QEMU --
PRINT_QEMU_OUTPUT --

BUT in common.rc we will have:
	GDB=""
         if [ ! -z ${GDB_OPTIONS} ]; then # <---- still set!
             GDB="gdbserver ${GDB_OPTIONS}"
         fi

so gsdbserver will be set anyways, and the test will block waiting for a 
gdb connection.

Therefore we need that elif.

> 
>> +
>>           if valgrind:
>>               self.valgrind_qemu = 'y'
>> @@ -269,7 +280,9 @@ def print_env(self) -> None:
>>   PLATFORM      -- {platform}
>>   TEST_DIR      -- {TEST_DIR}
>>   SOCK_DIR      -- {SOCK_DIR}
>> -SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}"""
>> +SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}
>> +GDB_OPTIONS   -- {GDB_OPTIONS}
> 
> Not sure we need to see this additional line every time.. Maybe, show it 
> only when gdb is set?

I think it can be helpful to remind the users what is not set and what 
is available to them (yes, one can also do ./check --help or read the 
docs but this is something even the laziest cannot unsee).

Thank you,
Emanuele
> 
>> +"""
>>           args = collections.defaultdict(str, self.get_env())
>>
> 
>
Vladimir Sementsov-Ogievskiy May 27, 2021, 12:17 p.m. UTC | #7
27.05.2021 14:06, Emanuele Giuseppe Esposito wrote:
> 
> 
> On 26/05/2021 21:15, Vladimir Sementsov-Ogievskiy wrote:
>> 20.05.2021 10:52, Emanuele Giuseppe Esposito wrote:
>>> Define -gdb flag and GDB_OPTIONS environment variable
>>> to python tests to attach a gdbserver to each qemu instance.
>>> This patch only adds and parses this flag, it does not yet add
>>> the implementation for it.
>>>
>>> if -gdb is not provided but $GDB_OPTIONS is set, ignore the
>>> environment variable.
>>>
>>> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
>>> ---
>>>   tests/qemu-iotests/check      |  6 +++++-
>>>   tests/qemu-iotests/iotests.py |  5 +++++
>>>   tests/qemu-iotests/testenv.py | 19 ++++++++++++++++---
>>>   3 files changed, 26 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
>>> index d1c87ceaf1..b9820fdaaf 100755
>>> --- a/tests/qemu-iotests/check
>>> +++ b/tests/qemu-iotests/check
>>> @@ -33,6 +33,9 @@ def make_argparser() -> argparse.ArgumentParser:
>>>                      help='pretty print output for make check')
>>>       p.add_argument('-d', dest='debug', action='store_true', help='debug')
>>> +    p.add_argument('-gdb', action='store_true',
>>> +                   help="start gdbserver with $GDB_OPTIONS options \
>>> +                        ('localhost:12345' if $GDB_OPTIONS is empty)")
>>>       p.add_argument('-misalign', action='store_true',
>>>                      help='misalign memory allocations')
>>>       p.add_argument('--color', choices=['on', 'off', 'auto'],
>>> @@ -112,7 +115,8 @@ if __name__ == '__main__':
>>>       env = TestEnv(imgfmt=args.imgfmt, imgproto=args.imgproto,
>>>                     aiomode=args.aiomode, cachemode=args.cachemode,
>>>                     imgopts=args.imgopts, misalign=args.misalign,
>>> -                  debug=args.debug, valgrind=args.valgrind)
>>> +                  debug=args.debug, valgrind=args.valgrind,
>>> +                  gdb=args.gdb)
>>>       testfinder = TestFinder(test_dir=env.source_iotests)
>>> diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
>>> index 5d78de0f0b..d667fde6f8 100644
>>> --- a/tests/qemu-iotests/iotests.py
>>> +++ b/tests/qemu-iotests/iotests.py
>>> @@ -75,6 +75,11 @@
>>>   qemu_prog = os.environ.get('QEMU_PROG', 'qemu')
>>>   qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ')
>>> +gdb_qemu_env = os.environ.get('GDB_OPTIONS')
>>
>> should we specify a default? otherwise get() should raise an exception when GDB_OPTIONS is not set..
>>
>> or you need os.getenv, which will return None if variable is absent.
> 
> No, os.environ.get does not raise any exception. I tested it myself, plus:
> https://stackoverflow.com/questions/16924471/difference-between-os-getenv-and-os-environ-get

Ah, I'm wrong than. OK.

> 
>>
>>> +qemu_gdb = []
>>> +if gdb_qemu_env:
>>> +    qemu_gdb = ['gdbserver'] + gdb_qemu_env.strip().split(' ')
>>> +
>>>   imgfmt = os.environ.get('IMGFMT', 'raw')
>>>   imgproto = os.environ.get('IMGPROTO', 'file')
>>>   output_dir = os.environ.get('OUTPUT_DIR', '.')
>>> diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py
>>> index 6d27712617..49ddd586ef 100644
>>> --- a/tests/qemu-iotests/testenv.py
>>> +++ b/tests/qemu-iotests/testenv.py
>>> @@ -27,6 +27,7 @@
>>>   import glob
>>>   from typing import Dict, Any, Optional, ContextManager
>>> +DEF_GDB_OPTIONS = 'localhost:12345'
>>>   def isxfile(path: str) -> bool:
>>>       return os.path.isfile(path) and os.access(path, os.X_OK)
>>> @@ -72,7 +73,8 @@ class TestEnv(ContextManager['TestEnv']):
>>>                        'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 'IMGPROTO',
>>>                        'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU',
>>>                        'CACHEMODE_IS_DEFAULT', 'IMGFMT_GENERIC', 'IMGOPTSSYNTAX',
>>> -                     'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 'MALLOC_PERTURB_']
>>> +                     'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 'MALLOC_PERTURB_',
>>> +                     'GDB_OPTIONS']
>>>       def get_env(self) -> Dict[str, str]:
>>>           env = {}
>>> @@ -163,7 +165,8 @@ def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
>>>                    imgopts: Optional[str] = None,
>>>                    misalign: bool = False,
>>>                    debug: bool = False,
>>> -                 valgrind: bool = False) -> None:
>>> +                 valgrind: bool = False,
>>> +                 gdb: bool = False) -> None:
>>>           self.imgfmt = imgfmt
>>>           self.imgproto = imgproto
>>>           self.aiomode = aiomode
>>> @@ -171,6 +174,14 @@ def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
>>>           self.misalign = misalign
>>>           self.debug = debug
>>> +        if gdb:
>>> +            self.gdb_options = os.environ.get('GDB_OPTIONS', DEF_GDB_OPTIONS)
>>
>> everywhere in the file os.getenv is used, let's be consistent on it.
>>
>>> +            if not self.gdb_options:
>>> +                # cover the case 'export GDB_OPTIONS='
>>> +                self.gdb_options = DEF_GDB_OPTIONS
>>
>> Hmm, a bit strange to handle 'export X=' only for this new variable, we don't have such logic for other variables.. I'm not against still, if you need it.
>>
>>> +        elif 'GDB_OPTIONS' in os.environ:
>>> +            del os.environ['GDB_OPTIONS']
>>
>> Don't need to remove variable from envirton, it has no effect. The task of TestEnv class is to prepare environment in its attributes, listed in env_variables. Then prepared attributes are available through get_env(). So if you don't want to provide GDB_OPTIONS, it's enough to not initialize self.gdb_options. So, just drop "elif:" branch.
> 
> You forget that there are bash tests :)

It shouldn't differ, environment is prepared in the same way for bash and python tests

> Think about the following case:
> 
> # export GDB_OPTIONS="localhost:1236"
> 
> # ./check -qcow2 007 # a script test
> 
> The output will contain:
> GDB_OPTIONS   --
> VALGRIND_QEMU --
> PRINT_QEMU_OUTPUT --
> 
> BUT in common.rc we will have:
>      GDB=""
>          if [ ! -z ${GDB_OPTIONS} ]; then # <---- still set!

Ah, I thought  that we work through testenv.get_env.. But we have testenv.prepare_subprocess, which propagates the original environment too..

the bit I don't like in this all is inconsistency in interfaces of check and tests:

New interface of check:

with -gdb option use GDB_OPTIONS or default value to start gdbserver
without -gdb option ignore GDB_OPTIONS

New interface of tests:

with GDB_OPTIONS run gdbserver
without GDB_OPTIONS don't run gdbserver

So, GDB_OPTIONS is different thing for tests and for check script.


I'd go another way:

Add GDB option (boolean false/true, default false)
Add GDB_OPTIONS (string, default localhost:1236)

Add -gdb argument to check, which is an alternative way to set GDB variable.

This way environment-variables interface is similar for tests and ./check, and we don't need to drop a variable from the environ, and new variable behave similar to existing variables, doesn't introduce new logic.

But that all don't worth arguing. I'm OK with patch as is.

>              GDB="gdbserver ${GDB_OPTIONS}"
>          fi
> 
> so gsdbserver will be set anyways, and the test will block waiting for a gdb connection.
> 
> Therefore we need that elif.
> 
>>
>>> +
>>>           if valgrind:
>>>               self.valgrind_qemu = 'y'
>>> @@ -269,7 +280,9 @@ def print_env(self) -> None:
>>>   PLATFORM      -- {platform}
>>>   TEST_DIR      -- {TEST_DIR}
>>>   SOCK_DIR      -- {SOCK_DIR}
>>> -SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}"""
>>> +SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}
>>> +GDB_OPTIONS   -- {GDB_OPTIONS}
>>
>> Not sure we need to see this additional line every time.. Maybe, show it only when gdb is set?
> 
> I think it can be helpful to remind the users what is not set and what is available to them (yes, one can also do ./check --help or read the docs but this is something even the laziest cannot unsee).
> 
> Thank you,
> Emanuele
>>
>>> +"""
>>>           args = collections.defaultdict(str, self.get_env())
>>>
>>
>>
>
Vladimir Sementsov-Ogievskiy May 28, 2021, 4:26 p.m. UTC | #8
20.05.2021 10:52, Emanuele Giuseppe Esposito wrote:
> Define -gdb flag and GDB_OPTIONS environment variable
> to python tests to attach a gdbserver to each qemu instance.
> This patch only adds and parses this flag, it does not yet add
> the implementation for it.
> 
> if -gdb is not provided but $GDB_OPTIONS is set, ignore the
> environment variable.
> 
> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
> ---
>   tests/qemu-iotests/check      |  6 +++++-
>   tests/qemu-iotests/iotests.py |  5 +++++
>   tests/qemu-iotests/testenv.py | 19 ++++++++++++++++---
>   3 files changed, 26 insertions(+), 4 deletions(-)
> 
> diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
> index d1c87ceaf1..b9820fdaaf 100755
> --- a/tests/qemu-iotests/check
> +++ b/tests/qemu-iotests/check
> @@ -33,6 +33,9 @@ def make_argparser() -> argparse.ArgumentParser:
>                      help='pretty print output for make check')
>   
>       p.add_argument('-d', dest='debug', action='store_true', help='debug')
> +    p.add_argument('-gdb', action='store_true',
> +                   help="start gdbserver with $GDB_OPTIONS options \
> +                        ('localhost:12345' if $GDB_OPTIONS is empty)")
>       p.add_argument('-misalign', action='store_true',
>                      help='misalign memory allocations')
>       p.add_argument('--color', choices=['on', 'off', 'auto'],
> @@ -112,7 +115,8 @@ if __name__ == '__main__':
>       env = TestEnv(imgfmt=args.imgfmt, imgproto=args.imgproto,
>                     aiomode=args.aiomode, cachemode=args.cachemode,
>                     imgopts=args.imgopts, misalign=args.misalign,
> -                  debug=args.debug, valgrind=args.valgrind)
> +                  debug=args.debug, valgrind=args.valgrind,
> +                  gdb=args.gdb)
>   
>       testfinder = TestFinder(test_dir=env.source_iotests)
>   
> diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
> index 5d78de0f0b..d667fde6f8 100644
> --- a/tests/qemu-iotests/iotests.py
> +++ b/tests/qemu-iotests/iotests.py
> @@ -75,6 +75,11 @@
>   qemu_prog = os.environ.get('QEMU_PROG', 'qemu')
>   qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ')
>   
> +gdb_qemu_env = os.environ.get('GDB_OPTIONS')
> +qemu_gdb = []
> +if gdb_qemu_env:
> +    qemu_gdb = ['gdbserver'] + gdb_qemu_env.strip().split(' ')
> +
>   imgfmt = os.environ.get('IMGFMT', 'raw')
>   imgproto = os.environ.get('IMGPROTO', 'file')
>   output_dir = os.environ.get('OUTPUT_DIR', '.')
> diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py
> index 6d27712617..49ddd586ef 100644
> --- a/tests/qemu-iotests/testenv.py
> +++ b/tests/qemu-iotests/testenv.py
> @@ -27,6 +27,7 @@
>   import glob
>   from typing import Dict, Any, Optional, ContextManager
>   
> +DEF_GDB_OPTIONS = 'localhost:12345'
>   
>   def isxfile(path: str) -> bool:
>       return os.path.isfile(path) and os.access(path, os.X_OK)
> @@ -72,7 +73,8 @@ class TestEnv(ContextManager['TestEnv']):
>                        'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 'IMGPROTO',
>                        'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU',
>                        'CACHEMODE_IS_DEFAULT', 'IMGFMT_GENERIC', 'IMGOPTSSYNTAX',
> -                     'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 'MALLOC_PERTURB_']
> +                     'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 'MALLOC_PERTURB_',
> +                     'GDB_OPTIONS']
>   
>       def get_env(self) -> Dict[str, str]:
>           env = {}
> @@ -163,7 +165,8 @@ def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
>                    imgopts: Optional[str] = None,
>                    misalign: bool = False,
>                    debug: bool = False,
> -                 valgrind: bool = False) -> None:
> +                 valgrind: bool = False,
> +                 gdb: bool = False) -> None:
>           self.imgfmt = imgfmt
>           self.imgproto = imgproto
>           self.aiomode = aiomode
> @@ -171,6 +174,14 @@ def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
>           self.misalign = misalign
>           self.debug = debug
>   
> +        if gdb:
> +            self.gdb_options = os.environ.get('GDB_OPTIONS', DEF_GDB_OPTIONS)
> +            if not self.gdb_options:
> +                # cover the case 'export GDB_OPTIONS='
> +                self.gdb_options = DEF_GDB_OPTIONS
> +        elif 'GDB_OPTIONS' in os.environ:

please add a comment:

  # to not propagate it in prepare_subprocess()

> +            del os.environ['GDB_OPTIONS']
> +
>           if valgrind:
>               self.valgrind_qemu = 'y'
>   
> @@ -269,7 +280,9 @@ def print_env(self) -> None:
>   PLATFORM      -- {platform}
>   TEST_DIR      -- {TEST_DIR}
>   SOCK_DIR      -- {SOCK_DIR}
> -SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}"""
> +SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}
> +GDB_OPTIONS   -- {GDB_OPTIONS}
> +"""
>   
>           args = collections.defaultdict(str, self.get_env())
>   
> 

Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
diff mbox series

Patch

diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
index d1c87ceaf1..b9820fdaaf 100755
--- a/tests/qemu-iotests/check
+++ b/tests/qemu-iotests/check
@@ -33,6 +33,9 @@  def make_argparser() -> argparse.ArgumentParser:
                    help='pretty print output for make check')
 
     p.add_argument('-d', dest='debug', action='store_true', help='debug')
+    p.add_argument('-gdb', action='store_true',
+                   help="start gdbserver with $GDB_OPTIONS options \
+                        ('localhost:12345' if $GDB_OPTIONS is empty)")
     p.add_argument('-misalign', action='store_true',
                    help='misalign memory allocations')
     p.add_argument('--color', choices=['on', 'off', 'auto'],
@@ -112,7 +115,8 @@  if __name__ == '__main__':
     env = TestEnv(imgfmt=args.imgfmt, imgproto=args.imgproto,
                   aiomode=args.aiomode, cachemode=args.cachemode,
                   imgopts=args.imgopts, misalign=args.misalign,
-                  debug=args.debug, valgrind=args.valgrind)
+                  debug=args.debug, valgrind=args.valgrind,
+                  gdb=args.gdb)
 
     testfinder = TestFinder(test_dir=env.source_iotests)
 
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 5d78de0f0b..d667fde6f8 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -75,6 +75,11 @@ 
 qemu_prog = os.environ.get('QEMU_PROG', 'qemu')
 qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ')
 
+gdb_qemu_env = os.environ.get('GDB_OPTIONS')
+qemu_gdb = []
+if gdb_qemu_env:
+    qemu_gdb = ['gdbserver'] + gdb_qemu_env.strip().split(' ')
+
 imgfmt = os.environ.get('IMGFMT', 'raw')
 imgproto = os.environ.get('IMGPROTO', 'file')
 output_dir = os.environ.get('OUTPUT_DIR', '.')
diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py
index 6d27712617..49ddd586ef 100644
--- a/tests/qemu-iotests/testenv.py
+++ b/tests/qemu-iotests/testenv.py
@@ -27,6 +27,7 @@ 
 import glob
 from typing import Dict, Any, Optional, ContextManager
 
+DEF_GDB_OPTIONS = 'localhost:12345'
 
 def isxfile(path: str) -> bool:
     return os.path.isfile(path) and os.access(path, os.X_OK)
@@ -72,7 +73,8 @@  class TestEnv(ContextManager['TestEnv']):
                      'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 'IMGPROTO',
                      'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU',
                      'CACHEMODE_IS_DEFAULT', 'IMGFMT_GENERIC', 'IMGOPTSSYNTAX',
-                     'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 'MALLOC_PERTURB_']
+                     'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 'MALLOC_PERTURB_',
+                     'GDB_OPTIONS']
 
     def get_env(self) -> Dict[str, str]:
         env = {}
@@ -163,7 +165,8 @@  def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
                  imgopts: Optional[str] = None,
                  misalign: bool = False,
                  debug: bool = False,
-                 valgrind: bool = False) -> None:
+                 valgrind: bool = False,
+                 gdb: bool = False) -> None:
         self.imgfmt = imgfmt
         self.imgproto = imgproto
         self.aiomode = aiomode
@@ -171,6 +174,14 @@  def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
         self.misalign = misalign
         self.debug = debug
 
+        if gdb:
+            self.gdb_options = os.environ.get('GDB_OPTIONS', DEF_GDB_OPTIONS)
+            if not self.gdb_options:
+                # cover the case 'export GDB_OPTIONS='
+                self.gdb_options = DEF_GDB_OPTIONS
+        elif 'GDB_OPTIONS' in os.environ:
+            del os.environ['GDB_OPTIONS']
+
         if valgrind:
             self.valgrind_qemu = 'y'
 
@@ -269,7 +280,9 @@  def print_env(self) -> None:
 PLATFORM      -- {platform}
 TEST_DIR      -- {TEST_DIR}
 SOCK_DIR      -- {SOCK_DIR}
-SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}"""
+SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}
+GDB_OPTIONS   -- {GDB_OPTIONS}
+"""
 
         args = collections.defaultdict(str, self.get_env())