qemu-commits
[Top][All Lists]
Advanced

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

[Qemu-commits] [qemu/qemu] 01afa7: scripts/performance: Add dissect.py s


From: Peter Maydell
Subject: [Qemu-commits] [qemu/qemu] 01afa7: scripts/performance: Add dissect.py script
Date: Wed, 15 Jul 2020 06:00:30 -0700

  Branch: refs/heads/master
  Home:   https://github.com/qemu/qemu
  Commit: 01afa757b6f1b8c7858cc29b8332e9fb6aa1e16f
      
https://github.com/qemu/qemu/commit/01afa757b6f1b8c7858cc29b8332e9fb6aa1e16f
  Author: Ahmed Karaman <ahmedkhaledkaraman@gmail.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    A scripts/performance/dissect.py

  Log Message:
  -----------
  scripts/performance: Add dissect.py script

Python script that dissects QEMU execution into three main phases:
code generation, JIT execution and helpers execution.

Syntax:
dissect.py [-h] -- <qemu executable> [<qemu executable options>] \
                 <target executable> [<target executable options>]

[-h] - Print the script arguments help message.

Example of usage:
dissect.py -- qemu-arm coulomb_double-arm

Example output:
Total Instructions:        4,702,865,362

Code Generation:             115,819,309         2.463%
JIT Execution:             1,081,980,528        23.007%
Helpers:                   3,505,065,525        74.530%

Signed-off-by: Ahmed Karaman <ahmedkhaledkaraman@gmail.com>
Reviewed-by: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-Id: <20200709052055.2650-2-ahmedkhaledkaraman@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: 14661d93d787846833b0e62d5995195c8851f741
      
https://github.com/qemu/qemu/commit/14661d93d787846833b0e62d5995195c8851f741
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/machine.py

  Log Message:
  -----------
  python/machine.py: consolidate _post_shutdown()

Move more cleanup actions into _post_shutdown. As a change, if QEMU
should so happen to be terminated during a call to wait(), that event
will now be logged.

This is not likely to occur during normative use.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Tested-by: Cleber Rosa <crosa@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-Id: <20200710050649.32434-2-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: 671940e633b83ac489e0b4bb407749723ff8a879
      
https://github.com/qemu/qemu/commit/671940e633b83ac489e0b4bb407749723ff8a879
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/machine.py

  Log Message:
  -----------
  python/machine.py: Close QMP socket in cleanup

It's not important to do this before waiting for the process to exit, so
it can be done during generic post-shutdown cleanup.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Tested-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20200710050649.32434-3-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: e2c97f161294c702ee4a2dd08532d5df67f6bff4
      
https://github.com/qemu/qemu/commit/e2c97f161294c702ee4a2dd08532d5df67f6bff4
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/machine.py

  Log Message:
  -----------
  python/machine.py: Add _early_cleanup hook

Some parts of cleanup need to occur prior to shutdown, otherwise
shutdown might break. Move this into a suitably named method/callback.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Tested-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20200710050649.32434-4-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: 3a7d64b6fc8ddce3987005e0ee6eadbe2cbba5c8
      
https://github.com/qemu/qemu/commit/3a7d64b6fc8ddce3987005e0ee6eadbe2cbba5c8
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/machine.py

  Log Message:
  -----------
  python/machine.py: Perform early cleanup for wait() calls, too

This is primarily for consistency, and is a step towards wait() and
shutdown() sharing the same implementation so that the two cleanup paths
cannot diverge.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Tested-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20200710050649.32434-5-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: a3842cb078a195db0715b00edd7812adcb8b077f
      
https://github.com/qemu/qemu/commit/a3842cb078a195db0715b00edd7812adcb8b077f
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/machine.py

  Log Message:
  -----------
  python/machine.py: Prohibit multiple shutdown() calls

If the VM is not launched, don't try to shut it down. As a change,
_post_shutdown now unconditionally also calls _early_cleanup in order to
offer comprehensive object cleanup in failure cases.

As a courtesy, treat it as a NOP instead of rejecting it as an
error. This is slightly nicer for acceptance tests where vm.shutdown()
is issued unconditionally in tearDown callbacks.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-Id: <20200710050649.32434-6-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: c9b3045bc2f52aca8825b6a04e9367b87d64d1cf
      
https://github.com/qemu/qemu/commit/c9b3045bc2f52aca8825b6a04e9367b87d64d1cf
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/machine.py

  Log Message:
  -----------
  python/machine.py: Add a configurable timeout to shutdown()

Three seconds is hardcoded. Use it as a default parameter instead, and use that
value for both waits that may occur in the function.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20200710050649.32434-7-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: 895280593139a1c34e59526835ba8fda903f8aaa
      
https://github.com/qemu/qemu/commit/895280593139a1c34e59526835ba8fda903f8aaa
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/machine.py

  Log Message:
  -----------
  python/machine.py: Make wait() call shutdown()

At this point, shutdown(has_quit=True) and wait() do essentially the
same thing; they perform cleanup without actually instructing QEMU to
quit.

Define one in terms of the other.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Tested-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20200710050649.32434-8-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: a0690c39006b897d6453daf591909948ac553650
      
https://github.com/qemu/qemu/commit/a0690c39006b897d6453daf591909948ac553650
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M tests/acceptance/boot_linux_console.py
    M tests/acceptance/linux_ssh_mips_malta.py

  Log Message:
  -----------
  tests/acceptance: wait() instead of shutdown() where appropriate

When issuing 'reboot' to a VM with the no-reboot option, that VM will
exit. When then issuing a shutdown command, the cleanup may race.

Add calls to vm.wait() which will gracefully mark the VM as having
exited. Subsequent vm.shutdown() calls in generic tearDown code will not
race when called after completion of the call.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Tested-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20200710050649.32434-9-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: fdb87f0dc2ed8e4f712a88fb5f9382ceea620223
      
https://github.com/qemu/qemu/commit/fdb87f0dc2ed8e4f712a88fb5f9382ceea620223
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M tests/acceptance/boot_linux_console.py

  Log Message:
  -----------
  tests/acceptance: Don't test reboot on cubieboard

cubieboard does not have a functioning reboot, it halts and QEMU does
not exit.

vm.shutdown() is modified in a forthcoming patch that makes it less tolerant
of race conditions on shutdown; tests should consciously decide to WAIT
or to SHUTDOWN qemu.

So long as this test is attempting to reboot, the correct choice would
be to WAIT for the VM to exit. However, since that's broken, we should
SHUTDOWN instead.

SHUTDOWN is indeed what already happens when the test performs teardown,
however, if anyone fixes cubieboard reboot in the future, this test will
develop a new race condition that might be hard to debug.

Therefore: remove the reboot test and make it obvious that the VM is
still running when the test concludes, where the test teardown will do
the right thing.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Tested-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20200710050649.32434-10-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: 193bf1c061ce0bb078ebc153facb9f31fe139d72
      
https://github.com/qemu/qemu/commit/193bf1c061ce0bb078ebc153facb9f31fe139d72
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/machine.py

  Log Message:
  -----------
  python/machine.py: split shutdown into hard and soft flavors

This is done primarily to avoid the 'bare except' pattern, which
suppresses all exceptions during shutdown and can obscure errors.

Replace this with a pattern that isolates the different kind of shutdown
paradigms (_hard_shutdown and _soft_shutdown), and a new fallback shutdown
handler (_do_shutdown) that gracefully attempts one before the other.

This split now also ensures that no matter what happens,
_post_shutdown() is always invoked.

shutdown() changes in behavior such that if it attempts to do a graceful
shutdown and is unable to, it will now always raise an exception to
indicate this. This can be avoided by the test writer in three ways:

1. If the VM is expected to have already exited or is in the process of
exiting, wait() can be used instead of shutdown() to clean up resources
instead. This helps avoid race conditions in shutdown.

2. If a test writer is expecting graceful shutdown to fail, shutdown
should be called in a try...except block.

3. If the test writer has no interest in performing a graceful shutdown
at all, kill() can be used instead.

Handling shutdown in this way makes it much more explicit which type of
shutdown we want and allows the library to report problems with this
process.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Tested-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20200710050649.32434-11-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: de6e08b5b987afbaf22e37e7f9e34421fb76ef3f
      
https://github.com/qemu/qemu/commit/de6e08b5b987afbaf22e37e7f9e34421fb76ef3f
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/machine.py

  Log Message:
  -----------
  python/machine.py: re-add sigkill warning suppression

If the user kills QEMU on purpose, we don't need to warn
them about that having happened: they know already.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20200710050649.32434-12-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: 04f0e36eba7b1a06e413a0690d4b1a24994d99fe
      
https://github.com/qemu/qemu/commit/04f0e36eba7b1a06e413a0690d4b1a24994d99fe
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/machine.py

  Log Message:
  -----------
  python/machine.py: change default wait timeout to 3 seconds

Machine.wait() does not appear to be used except in the acceptance tests,
and an infinite timeout by default in a test suite is not the most helpful.

Change it to 3 seconds, like the default shutdown timeout.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Tested-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20200710050649.32434-13-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: a5d76376d65d8777f28bb064412a8d72fa2c7953
      
https://github.com/qemu/qemu/commit/a5d76376d65d8777f28bb064412a8d72fa2c7953
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/qmp.py

  Log Message:
  -----------
  python/qmp.py: Define common types

Define some common types that we'll need to annotate a lot of other
functions going forward.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Message-Id: <20200710052220.3306-2-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: 2012453ddde0506d044d4739257227c6868028b6
      
https://github.com/qemu/qemu/commit/2012453ddde0506d044d4739257227c6868028b6
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M tests/qemu-iotests/iotests.py

  Log Message:
  -----------
  iotests.py: use qemu.qmp type aliases

iotests.py should use the type definitions from qmp.py instead of its
own.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Message-Id: <20200710052220.3306-3-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: e3a23b4803a3939c7e24e8946880f5ef369ef781
      
https://github.com/qemu/qemu/commit/e3a23b4803a3939c7e24e8946880f5ef369ef781
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/machine.py
    M python/qemu/qmp.py
    M scripts/render_block_graph.py

  Log Message:
  -----------
  python/qmp.py: re-absorb MonitorResponseError

When I initially split this out, I considered this more of a machine
error than a QMP protocol error, but I think that's misguided.

Move this back to qmp.py and name it QMPResponseError. Convert
qmp.command() to use this exception type.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Message-Id: <20200710052220.3306-4-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: ef5d474472426eda6abf8128cdb1d026af94862b
      
https://github.com/qemu/qemu/commit/ef5d474472426eda6abf8128cdb1d026af94862b
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/qmp.py

  Log Message:
  -----------
  python/qmp.py: Do not return None from cmd_obj

This makes typing the qmp library difficult, as it necessitates wrapping
Optional[] around the type for every return type up the stack. At some
point, it becomes difficult to discern or remember why it's None instead
of the expected object.

Use the python exception system to tell us exactly why we didn't get an
object. Remove this special-cased return.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Message-Id: <20200710052220.3306-5-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: 2e2d93051753067fc5b888fdc18831127a4a900e
      
https://github.com/qemu/qemu/commit/2e2d93051753067fc5b888fdc18831127a4a900e
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/qmp.py

  Log Message:
  -----------
  python/qmp.py: add casts to JSON deserialization

mypy and python type hints are not powerful enough to properly describe
JSON messages in Python 3.6. The best we can do, generally, is describe
them as Dict[str, Any].

Add casts to coerce this type for static analysis; but do NOT enforce
this type at runtime in any way.

Note: Python 3.8 adds a TypedDict construct which allows for the
description of more arbitrary Dictionary shapes. There is a third-party
module, "Pydantic", which is compatible with 3.6 that can be used
instead of the JSON library that parses JSON messages to fully-typed
Python objects, and may be preferable in some cases.

(That is well beyond the scope of this commit or series.)

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Message-Id: <20200710052220.3306-6-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: 84dcdf0887cdaaba7300442482c99e5064865a2d
      
https://github.com/qemu/qemu/commit/84dcdf0887cdaaba7300442482c99e5064865a2d
  Author: John Snow <jsnow@redhat.com>
  Date:   2020-07-14 (Tue, 14 Jul 2020)

  Changed paths:
    M python/qemu/qmp.py

  Log Message:
  -----------
  python/qmp.py: add QMPProtocolError

In the case that we receive a reply but are unable to understand it,
use this exception name to indicate that case.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Message-Id: <20200710052220.3306-7-jsnow@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>


  Commit: 673205379fb499d2b72f2985b47ec7114282f5fe
      
https://github.com/qemu/qemu/commit/673205379fb499d2b72f2985b47ec7114282f5fe
  Author: Peter Maydell <peter.maydell@linaro.org>
  Date:   2020-07-15 (Wed, 15 Jul 2020)

  Changed paths:
    M python/qemu/machine.py
    M python/qemu/qmp.py
    A scripts/performance/dissect.py
    M scripts/render_block_graph.py
    M tests/acceptance/boot_linux_console.py
    M tests/acceptance/linux_ssh_mips_malta.py
    M tests/qemu-iotests/iotests.py

  Log Message:
  -----------
  Merge remote-tracking branch 
'remotes/philmd-gitlab/tags/python-next-20200714' into staging

Python patches for 5.1

- Reduce race conditions on QEMUMachine::shutdown()

 1. Remove the "bare except" pattern in the existing shutdown code,
    which can mask problems and make debugging difficult.
 2. Ensure that post-shutdown cleanup is always performed, even when
    graceful termination fails.
 3. Unify cleanup paths such that no matter how the VM is terminated,
    the same functions and steps are always taken to reset the object
    state.
 4. Rewrite shutdown() such that any error encountered when attempting
    a graceful shutdown will be raised as an AbnormalShutdown exception.
    The pythonic idiom is to allow the caller to decide if this is a
    problem or not.

- Modify part of the python/qemu library to comply with:

  . mypy --strict
  . pylint
  . flake8

- Script for the TCG Continuous Benchmarking project that uses
  callgrind to dissect QEMU execution into three main phases:

  . code generation
  . JIT execution
  . helpers execution

CI jobs results:
. https://cirrus-ci.com/build/5421349961203712
. https://gitlab.com/philmd/qemu/-/pipelines/166556001
. https://travis-ci.org/github/philmd/qemu/builds/708102347

# gpg: Signature made Tue 14 Jul 2020 21:40:05 BST
# gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" 
[full]
# Primary key fingerprint: FAAB E75E 1291 7221 DCFD  6BB2 E3E3 2C2C DEAD C0DE

* remotes/philmd-gitlab/tags/python-next-20200714:
  python/qmp.py: add QMPProtocolError
  python/qmp.py: add casts to JSON deserialization
  python/qmp.py: Do not return None from cmd_obj
  python/qmp.py: re-absorb MonitorResponseError
  iotests.py: use qemu.qmp type aliases
  python/qmp.py: Define common types
  python/machine.py: change default wait timeout to 3 seconds
  python/machine.py: re-add sigkill warning suppression
  python/machine.py: split shutdown into hard and soft flavors
  tests/acceptance: Don't test reboot on cubieboard
  tests/acceptance: wait() instead of shutdown() where appropriate
  python/machine.py: Make wait() call shutdown()
  python/machine.py: Add a configurable timeout to shutdown()
  python/machine.py: Prohibit multiple shutdown() calls
  python/machine.py: Perform early cleanup for wait() calls, too
  python/machine.py: Add _early_cleanup hook
  python/machine.py: Close QMP socket in cleanup
  python/machine.py: consolidate _post_shutdown()
  scripts/performance: Add dissect.py script

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>


Compare: https://github.com/qemu/qemu/compare/3a9163af4e3d...673205379fb4



reply via email to

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