blob: 74c3c355a92d6b7c198a10f31e56ad181a1023c2 [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
22def extract_profile_data(device_output_file):
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:
29 device_output_file: File that contains the device output.
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 """
36 lines = device_output_file.read().decode('utf-8', 'ignore').splitlines()
37 for i, line in zip(reversed(range(len(lines))), reversed(lines)):
38 match = re.match(
39 r"""
40 LLVM\ profile\ data
41 \ \(length:\ (?P<length>\d*),
42 \ CRC32:\ (?P<checksum>[0-9A-F]*)\):
43 """, line, re.VERBOSE)
44 if match:
45 exp_length = int(match.group('length'))
46 exp_checksum = match.group('checksum')
47 byte_array = bytes.fromhex(lines[i + 1])
48 break
49 # Check if output has LLVM profile data
50 if not match:
51 raise ValueError(
52 'Could not detect the LLVM profile data in device output.')
53 # Check length
54 act_length = len(byte_array)
55 if act_length != exp_length:
56 raise ValueError(('Length check failed! ',
57 f'Expected: {exp_length}, actual: {act_length}.'))
58 # Check checksum
59 act_checksum = zlib.crc32(byte_array).to_bytes(4,
60 byteorder='little',
61 signed=False).hex().upper()
62 if act_checksum != exp_checksum:
63 raise ValueError(
64 ('Checksum check failed! ',
65 f'Expected: {exp_checksum}, actual: {act_checksum}.'))
66 return byte_array
67
68
69def main():
70 """Parses command line arguments and extracts the profile data from device
71 output."""
72 argparser = argparse.ArgumentParser(
73 description='Extract LLVM profile data from device output.')
74 argparser.add_argument(dest='output_file',
75 type=argparse.FileType('wb'),
76 default=sys.stdout,
77 help='output file for writing LLVM profile data')
78 argparser.add_argument('--input_file',
79 type=argparse.FileType('rb'),
80 default=sys.stdin.buffer,
81 help='device output')
82 args = argparser.parse_args()
83
84 args.output_file.write(extract_profile_data(args.input_file))
85
86
87if __name__ == '__main__':
88 main()