qemu_image.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. # Copyright 2020 The Chromium Authors. All rights reserved.
  2. # Use of this source code is governed by a BSD-style license that can be
  3. # found in the LICENSE file.
  4. """Workaround for qemu-img bug on arm64 platforms with multiple cores.
  5. Runs qemu-img command with timeout and retries the command if it hangs.
  6. See:
  7. crbug.com/1046861 QEMU is out of date; current version of qemu-img
  8. is unstable
  9. https://bugs.launchpad.net/qemu/+bug/1805256 qemu-img hangs on
  10. rcu_call_ready_event logic in Aarch64 when converting images
  11. TODO(crbug.com/1046861): Remove this workaround when the bug is fixed.
  12. """
  13. import logging
  14. import subprocess
  15. import tempfile
  16. import time
  17. # qemu-img p99 run time on Cavium ThunderX2 servers is 26 seconds.
  18. # Using 2x the p99 time as the timeout.
  19. QEMU_IMG_TIMEOUT_SEC = 52
  20. def _ExecQemuImgWithTimeout(command):
  21. """Execute qemu-img command in subprocess with timeout.
  22. Returns: None if command timed out or return code if command completed.
  23. """
  24. logging.info('qemu-img starting')
  25. command_output_file = tempfile.NamedTemporaryFile('w')
  26. p = subprocess.Popen(command, stdout=command_output_file,
  27. stderr=subprocess.STDOUT)
  28. start_sec = time.time()
  29. while p.poll() is None and time.time() - start_sec < QEMU_IMG_TIMEOUT_SEC:
  30. time.sleep(1)
  31. stop_sec = time.time()
  32. logging.info('qemu-img duration: %f' % float(stop_sec - start_sec))
  33. if p.poll() is None:
  34. returncode = None
  35. p.kill()
  36. p.wait()
  37. else:
  38. returncode = p.returncode
  39. log_level = logging.WARN if returncode else logging.DEBUG
  40. for line in open(command_output_file.name, 'r'):
  41. logging.log(log_level, 'qemu-img stdout: ' + line.strip())
  42. return returncode
  43. def ExecQemuImgWithRetry(command):
  44. """ Execute qemu-img command in subprocess with 2 retries.
  45. Raises CalledProcessError if command does not complete successfully.
  46. """
  47. tries = 0
  48. status = None
  49. while status is None and tries <= 2:
  50. tries += 1
  51. status = _ExecQemuImgWithTimeout(command)
  52. if status is None:
  53. raise subprocess.CalledProcessError(-1, command)
  54. if status:
  55. raise subprocess.CalledProcessError(status, command)