blob: 5b8bbf3ef37fbe821b2ee1439c4e8497a93cb4d9 [file] [log] [blame]
#!/usr/bin/env python3
#
# validate-memory-counts.py: check we instrumented memory properly
#
# This program takes two inputs:
# - the mem plugin output
# - the memory binary output
#
# Copyright (C) 2024 Linaro Ltd
#
# SPDX-License-Identifier: GPL-2.0-or-later
import sys
from argparse import ArgumentParser
def extract_counts(path):
"""
Load the output from path and extract the lines containing:
Test data start: 0x40214000
Test data end: 0x40218001
Test data read: 2522280
Test data write: 262111
From the stream of data. Extract the values for use in the
validation function.
"""
start_address = None
end_address = None
read_count = 0
write_count = 0
with open(path, 'r') as f:
for line in f:
if line.startswith("Test data start:"):
start_address = int(line.split(':')[1].strip(), 16)
elif line.startswith("Test data end:"):
end_address = int(line.split(':')[1].strip(), 16)
elif line.startswith("Test data read:"):
read_count = int(line.split(':')[1].strip())
elif line.startswith("Test data write:"):
write_count = int(line.split(':')[1].strip())
return start_address, end_address, read_count, write_count
def parse_plugin_output(path, start, end):
"""
Load the plugin output from path in the form of:
Region Base, Reads, Writes, Seen all
0x0000000040004000, 31093, 0, false
0x0000000040214000, 2522280, 278579, true
0x0000000040000000, 137398, 0, false
0x0000000040210000, 54727397, 33721956, false
And extract the ranges that match test data start and end and
return the results.
"""
total_reads = 0
total_writes = 0
seen_all = False
with open(path, 'r') as f:
next(f) # Skip the header
for line in f:
if line.startswith("Region Base"):
continue
parts = line.strip().split(', ')
if len(parts) != 4:
continue
region_base = int(parts[0], 16)
reads = int(parts[1])
writes = int(parts[2])
if start <= region_base < end: # Checking if within range
total_reads += reads
total_writes += writes
seen_all = parts[3] == "true"
return total_reads, total_writes, seen_all
def main() -> None:
"""
Process the arguments, injest the program and plugin out and
verify they match up and report if they do not.
"""
parser = ArgumentParser(description="Validate memory instrumentation")
parser.add_argument('test_output',
help="The output from the test itself")
parser.add_argument('plugin_output',
help="The output from memory plugin")
parser.add_argument('--bss-cleared',
action='store_true',
help='Assume bss was cleared (and adjusts counts).')
args = parser.parse_args()
# Extract counts from memory binary
start, end, exp_reads, exp_writes = extract_counts(args.test_output)
# Some targets clear BSS before running but the test doesn't know
# that so we adjust it by the size of the test region.
if args.bss_cleared:
exp_writes += 16384
if start is None or end is None:
print("Failed to test_data boundaries from output.")
sys.exit(1)
# Parse plugin output
preads, pwrites, seen_all = parse_plugin_output(args.plugin_output,
start, end)
if not seen_all:
print("Fail: didn't instrument all accesses to test_data.")
sys.exit(1)
# Compare and report
if preads == exp_reads and pwrites == exp_writes:
sys.exit(0)
else:
print("Fail: The memory reads and writes count does not match.")
print(f"Expected Reads: {exp_reads}, Actual Reads: {preads}")
print(f"Expected Writes: {exp_writes}, Actual Writes: {pwrites}")
sys.exit(1)
if __name__ == "__main__":
main()