Kaido Kert | 5ac52c4 | 2021-05-14 12:23:37 -0700 | [diff] [blame] | 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 | |
| 5 | """Converts exceptions to return codes and prints error messages. |
| 6 | |
| 7 | This makes it easier to query build tables for particular error types as |
| 8 | exit codes are visible to queries while exception stack traces are not.""" |
| 9 | |
| 10 | import errno |
| 11 | import fcntl |
| 12 | import logging |
| 13 | import os |
| 14 | import subprocess |
| 15 | import sys |
| 16 | import traceback |
| 17 | |
| 18 | from target import FuchsiaTargetException |
| 19 | |
| 20 | def _PrintException(value, trace): |
| 21 | """Prints stack trace and error message for the current exception.""" |
| 22 | |
| 23 | traceback.print_tb(trace) |
| 24 | print(str(value)) |
| 25 | |
| 26 | |
| 27 | def IsStdoutBlocking(): |
| 28 | """Returns True if sys.stdout is blocking or False if non-blocking. |
| 29 | |
| 30 | sys.stdout should always be blocking. Non-blocking is associated with |
| 31 | intermittent IOErrors (crbug.com/1080858). |
| 32 | """ |
| 33 | |
| 34 | nonblocking = fcntl.fcntl(sys.stdout, fcntl.F_GETFL) & os.O_NONBLOCK |
| 35 | return not nonblocking |
| 36 | |
| 37 | |
| 38 | def HandleExceptionAndReturnExitCode(): |
| 39 | """Maps the current exception to a return code and prints error messages. |
| 40 | |
| 41 | Mapped exception types are assigned blocks of 8 return codes starting at 64. |
| 42 | The choice of 64 as the starting code is based on the Advanced Bash-Scripting |
| 43 | Guide (http://tldp.org/LDP/abs/html/exitcodes.html). |
| 44 | |
| 45 | A generic exception is mapped to the start of the block. More specific |
| 46 | exceptions are mapped to numbers inside the block. For example, a |
| 47 | FuchsiaTargetException is mapped to return code 64, unless it involves SSH |
| 48 | in which case it is mapped to return code 65. |
| 49 | |
| 50 | Exceptions not specifically mapped go to return code 1. |
| 51 | |
| 52 | Returns the mapped return code.""" |
| 53 | |
| 54 | (type, value, trace) = sys.exc_info() |
| 55 | _PrintException(value, trace) |
| 56 | |
| 57 | if type is FuchsiaTargetException: |
| 58 | if 'ssh' in str(value).lower(): |
| 59 | print('Error: FuchsiaTargetException: SSH to Fuchsia target failed.') |
| 60 | return 65 |
| 61 | return 64 |
| 62 | elif type is IOError: |
| 63 | if value.errno == errno.EAGAIN: |
| 64 | logging.info('Python print to sys.stdout probably failed') |
| 65 | if not IsStdoutBlocking(): |
| 66 | logging.warn('sys.stdout is non-blocking') |
| 67 | return 73 |
| 68 | return 72 |
| 69 | elif type is subprocess.CalledProcessError: |
| 70 | if os.path.basename(value.cmd[0]) == 'scp': |
| 71 | print('Error: scp operation failed - %s' % str(value)) |
| 72 | return 81 |
| 73 | if os.path.basename(value.cmd[0]) == 'qemu-img': |
| 74 | print('Error: qemu-img fuchsia image generation failed.') |
| 75 | return 82 |
| 76 | return 80 |
| 77 | else: |
| 78 | return 1 |