blob: a36025ee189d5f21f468bb32099b968f698cbb53 [file] [log] [blame]
#!/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):
"""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: 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.
"""
device_output = re.sub(r'^.*] (.*)$',
r'\1',
device_output,
flags=re.MULTILINE)
device_output = device_output.translate(
device_output.maketrans('', '', '\r\n'))
match = re.search(
r"""
LLVM\ profile\ data\ \(length:\ (?P<len>\d+)\ bytes,\ CRC32:\ (?P<crc>0x[0-9a-f]*)\):
(?P<data> 0x [0-9a-f]+)
\x04
""", device_output, re.VERBOSE)
if not match:
raise ValueError(
'Could not detect LLVM profile data in device output.')
exp_length = int(match.group('len'))
exp_checksum = int(match.group('crc'), 0)
byte_array = int(match.group('data'), 0).to_bytes(len(match.group('data')) // 2 - 1,
byteorder='little',
signed=False)
# 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)
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.read().decode('ascii', 'ignore')))
if __name__ == '__main__':
main()