blob: a36025ee189d5f21f468bb32099b968f698cbb53 [file] [log] [blame]
Alphan Ulusoydf4e3d82020-08-06 13:44:30 -04001#!/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
7The profile data obtained from the device is raw. Thus, it must be indexed
8before it can be used to generate coverage reports.
9
10Typical 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"""
16import argparse
17import zlib
18import re
19import sys
20
21
Alphan Ulusoy51fb9e22022-11-29 14:14:30 -050022def extract_profile_data(device_output):
Alphan Ulusoydf4e3d82020-08-06 13:44:30 -040023 """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 Ulusoy51fb9e22022-11-29 14:14:30 -050029 device_output: Device output.
Alphan Ulusoydf4e3d82020-08-06 13:44:30 -040030 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 Ulusoy51fb9e22022-11-29 14:14:30 -050036 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 Ulusoydf4e3d82020-08-06 13:44:30 -040048 if not match:
49 raise ValueError(
Alphan Ulusoy51fb9e22022-11-29 14:14:30 -050050 '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 Ulusoydf4e3d82020-08-06 13:44:30 -040056 # 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 Ulusoy51fb9e22022-11-29 14:14:30 -050062 act_checksum = zlib.crc32(byte_array)
Alphan Ulusoydf4e3d82020-08-06 13:44:30 -040063 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
70def 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 Ulusoy51fb9e22022-11-29 14:14:30 -050085 args.output_file.write(
86 extract_profile_data(args.input_file.read().decode('ascii', 'ignore')))
Alphan Ulusoydf4e3d82020-08-06 13:44:30 -040087
88
89if __name__ == '__main__':
90 main()