| #!/usr/bin/env python3 | 
 | # Copyright lowRISC contributors. | 
 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | # SPDX-License-Identifier: Apache-2.0 | 
 | """Parse device output to extract LLVM profile data. | 
 |  | 
 | The profile data obtained from the device is raw. Thus, it must be indexed | 
 | before it can be used to generate coverage reports. | 
 |  | 
 | Typical usage: | 
 |     stty -F /dev/ttyUSB0 icanon | 
 |     cat /dev/ttyUSB0 | ./device_profile_data.py foo.profraw | 
 |     llvm-profdata merge -sparse foo.profraw -o foo.profdata | 
 |     llvm-cov show OBJECT_FILE -instr-profile=foo.profdata | 
 | """ | 
 | import argparse | 
 | import zlib | 
 | import re | 
 | import sys | 
 |  | 
 |  | 
 | def extract_profile_data(device_output_file): | 
 |     """Parse device output to extract LLVM profile data. | 
 |  | 
 |     This function returns the LLVM profile data as a byte array after | 
 |     verifying its length and checksum. | 
 |  | 
 |     Args: | 
 |         device_output_file: File that contains the device output. | 
 |     Returns: | 
 |         LLVM profile data. | 
 |     Raises: | 
 |         ValueError: If LLVM profile data cannot be detected in the device | 
 |             output or its length or checksum is incorrect. | 
 |     """ | 
 |     lines = device_output_file.read().decode('utf-8', 'ignore').splitlines() | 
 |     for i, line in zip(reversed(range(len(lines))), reversed(lines)): | 
 |         match = re.match( | 
 |             r""" | 
 |                 LLVM\ profile\ data | 
 |                 \ \(length:\ (?P<length>\d*), | 
 |                 \ CRC32:\ (?P<checksum>[0-9A-F]*)\): | 
 |             """, line, re.VERBOSE) | 
 |         if match: | 
 |             exp_length = int(match.group('length')) | 
 |             exp_checksum = match.group('checksum') | 
 |             byte_array = bytes.fromhex(lines[i + 1]) | 
 |             break | 
 |     # Check if output has LLVM profile data | 
 |     if not match: | 
 |         raise ValueError( | 
 |             'Could not detect the LLVM profile data in device output.') | 
 |     # Check length | 
 |     act_length = len(byte_array) | 
 |     if act_length != exp_length: | 
 |         raise ValueError(('Length check failed! ', | 
 |                           f'Expected: {exp_length}, actual: {act_length}.')) | 
 |     # Check checksum | 
 |     act_checksum = zlib.crc32(byte_array).to_bytes(4, | 
 |                                                    byteorder='little', | 
 |                                                    signed=False).hex().upper() | 
 |     if act_checksum != exp_checksum: | 
 |         raise ValueError( | 
 |             ('Checksum check failed! ', | 
 |              f'Expected: {exp_checksum}, actual: {act_checksum}.')) | 
 |     return byte_array | 
 |  | 
 |  | 
 | def main(): | 
 |     """Parses command line arguments and extracts the profile data from device | 
 |     output.""" | 
 |     argparser = argparse.ArgumentParser( | 
 |         description='Extract LLVM profile data from device output.') | 
 |     argparser.add_argument(dest='output_file', | 
 |                            type=argparse.FileType('wb'), | 
 |                            default=sys.stdout, | 
 |                            help='output file for writing LLVM profile data') | 
 |     argparser.add_argument('--input_file', | 
 |                            type=argparse.FileType('rb'), | 
 |                            default=sys.stdin.buffer, | 
 |                            help='device output') | 
 |     args = argparser.parse_args() | 
 |  | 
 |     args.output_file.write(extract_profile_data(args.input_file)) | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |     main() |