Alphan Ulusoy | df4e3d8 | 2020-08-06 13:44:30 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # Copyright lowRISC contributors. |
| 3 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 4 | # SPDX-License-Identifier: Apache-2.0 |
| 5 | """Parse device output to extract LLVM profile data. |
| 6 | |
| 7 | The profile data obtained from the device is raw. Thus, it must be indexed |
| 8 | before it can be used to generate coverage reports. |
| 9 | |
| 10 | Typical usage: |
| 11 | stty -F /dev/ttyUSB0 icanon |
| 12 | cat /dev/ttyUSB0 | ./device_profile_data.py foo.profraw |
| 13 | llvm-profdata merge -sparse foo.profraw -o foo.profdata |
| 14 | llvm-cov show OBJECT_FILE -instr-profile=foo.profdata |
| 15 | """ |
| 16 | import argparse |
| 17 | import zlib |
| 18 | import re |
| 19 | import sys |
| 20 | |
| 21 | |
Alphan Ulusoy | 51fb9e2 | 2022-11-29 14:14:30 -0500 | [diff] [blame] | 22 | def extract_profile_data(device_output): |
Alphan Ulusoy | df4e3d8 | 2020-08-06 13:44:30 -0400 | [diff] [blame] | 23 | """Parse device output to extract LLVM profile data. |
| 24 | |
| 25 | This function returns the LLVM profile data as a byte array after |
| 26 | verifying its length and checksum. |
| 27 | |
| 28 | Args: |
Alphan Ulusoy | 51fb9e2 | 2022-11-29 14:14:30 -0500 | [diff] [blame] | 29 | device_output: Device output. |
Alphan Ulusoy | df4e3d8 | 2020-08-06 13:44:30 -0400 | [diff] [blame] | 30 | Returns: |
| 31 | LLVM profile data. |
| 32 | Raises: |
| 33 | ValueError: If LLVM profile data cannot be detected in the device |
| 34 | output or its length or checksum is incorrect. |
| 35 | """ |
Alphan Ulusoy | 51fb9e2 | 2022-11-29 14:14:30 -0500 | [diff] [blame] | 36 | device_output = re.sub(r'^.*] (.*)$', |
| 37 | r'\1', |
| 38 | device_output, |
| 39 | flags=re.MULTILINE) |
| 40 | device_output = device_output.translate( |
| 41 | device_output.maketrans('', '', '\r\n')) |
| 42 | match = re.search( |
| 43 | r""" |
| 44 | LLVM\ profile\ data\ \(length:\ (?P<len>\d+)\ bytes,\ CRC32:\ (?P<crc>0x[0-9a-f]*)\): |
| 45 | (?P<data> 0x [0-9a-f]+) |
| 46 | \x04 |
| 47 | """, device_output, re.VERBOSE) |
Alphan Ulusoy | df4e3d8 | 2020-08-06 13:44:30 -0400 | [diff] [blame] | 48 | if not match: |
| 49 | raise ValueError( |
Alphan Ulusoy | 51fb9e2 | 2022-11-29 14:14:30 -0500 | [diff] [blame] | 50 | 'Could not detect LLVM profile data in device output.') |
| 51 | exp_length = int(match.group('len')) |
| 52 | exp_checksum = int(match.group('crc'), 0) |
| 53 | byte_array = int(match.group('data'), 0).to_bytes(len(match.group('data')) // 2 - 1, |
| 54 | byteorder='little', |
| 55 | signed=False) |
Alphan Ulusoy | df4e3d8 | 2020-08-06 13:44:30 -0400 | [diff] [blame] | 56 | # Check length |
| 57 | act_length = len(byte_array) |
| 58 | if act_length != exp_length: |
| 59 | raise ValueError(('Length check failed! ', |
| 60 | f'Expected: {exp_length}, actual: {act_length}.')) |
| 61 | # Check checksum |
Alphan Ulusoy | 51fb9e2 | 2022-11-29 14:14:30 -0500 | [diff] [blame] | 62 | act_checksum = zlib.crc32(byte_array) |
Alphan Ulusoy | df4e3d8 | 2020-08-06 13:44:30 -0400 | [diff] [blame] | 63 | if act_checksum != exp_checksum: |
| 64 | raise ValueError( |
| 65 | ('Checksum check failed! ', |
| 66 | f'Expected: {exp_checksum}, actual: {act_checksum}.')) |
| 67 | return byte_array |
| 68 | |
| 69 | |
| 70 | def main(): |
| 71 | """Parses command line arguments and extracts the profile data from device |
| 72 | output.""" |
| 73 | argparser = argparse.ArgumentParser( |
| 74 | description='Extract LLVM profile data from device output.') |
| 75 | argparser.add_argument(dest='output_file', |
| 76 | type=argparse.FileType('wb'), |
| 77 | default=sys.stdout, |
| 78 | help='output file for writing LLVM profile data') |
| 79 | argparser.add_argument('--input_file', |
| 80 | type=argparse.FileType('rb'), |
| 81 | default=sys.stdin.buffer, |
| 82 | help='device output') |
| 83 | args = argparser.parse_args() |
| 84 | |
Alphan Ulusoy | 51fb9e2 | 2022-11-29 14:14:30 -0500 | [diff] [blame] | 85 | args.output_file.write( |
| 86 | extract_profile_data(args.input_file.read().decode('ascii', 'ignore'))) |
Alphan Ulusoy | df4e3d8 | 2020-08-06 13:44:30 -0400 | [diff] [blame] | 87 | |
| 88 | |
| 89 | if __name__ == '__main__': |
| 90 | main() |