1#!/usr/bin/env python3 2# 3# validate-memory-counts.py: check we instrumented memory properly 4# 5# This program takes two inputs: 6# - the mem plugin output 7# - the memory binary output 8# 9# Copyright (C) 2024 Linaro Ltd 10# 11# SPDX-License-Identifier: GPL-2.0-or-later 12 13import sys 14from argparse import ArgumentParser 15 16def extract_counts(path): 17 """ 18 Load the output from path and extract the lines containing: 19 20 Test data start: 0x40214000 21 Test data end: 0x40218001 22 Test data read: 2522280 23 Test data write: 262111 24 25 From the stream of data. Extract the values for use in the 26 validation function. 27 """ 28 start_address = None 29 end_address = None 30 read_count = 0 31 write_count = 0 32 with open(path, 'r') as f: 33 for line in f: 34 if line.startswith("Test data start:"): 35 start_address = int(line.split(':')[1].strip(), 16) 36 elif line.startswith("Test data end:"): 37 end_address = int(line.split(':')[1].strip(), 16) 38 elif line.startswith("Test data read:"): 39 read_count = int(line.split(':')[1].strip()) 40 elif line.startswith("Test data write:"): 41 write_count = int(line.split(':')[1].strip()) 42 return start_address, end_address, read_count, write_count 43 44 45def parse_plugin_output(path, start, end): 46 """ 47 Load the plugin output from path in the form of: 48 49 Region Base, Reads, Writes, Seen all 50 0x0000000040004000, 31093, 0, false 51 0x0000000040214000, 2522280, 278579, true 52 0x0000000040000000, 137398, 0, false 53 0x0000000040210000, 54727397, 33721956, false 54 55 And extract the ranges that match test data start and end and 56 return the results. 57 """ 58 total_reads = 0 59 total_writes = 0 60 seen_all = False 61 62 with open(path, 'r') as f: 63 next(f) # Skip the header 64 for line in f: 65 66 if line.startswith("Region Base"): 67 continue 68 69 parts = line.strip().split(', ') 70 if len(parts) != 4: 71 continue 72 73 region_base = int(parts[0], 16) 74 reads = int(parts[1]) 75 writes = int(parts[2]) 76 77 if start <= region_base < end: # Checking if within range 78 total_reads += reads 79 total_writes += writes 80 seen_all = parts[3] == "true" 81 82 return total_reads, total_writes, seen_all 83 84def main() -> None: 85 """ 86 Process the arguments, injest the program and plugin out and 87 verify they match up and report if they do not. 88 """ 89 parser = ArgumentParser(description="Validate memory instrumentation") 90 parser.add_argument('test_output', 91 help="The output from the test itself") 92 parser.add_argument('plugin_output', 93 help="The output from memory plugin") 94 parser.add_argument('--bss-cleared', 95 action='store_true', 96 help='Assume bss was cleared (and adjusts counts).') 97 98 args = parser.parse_args() 99 100 # Extract counts from memory binary 101 start, end, exp_reads, exp_writes = extract_counts(args.test_output) 102 103 # Some targets clear BSS before running but the test doesn't know 104 # that so we adjust it by the size of the test region. 105 if args.bss_cleared: 106 exp_writes += 16384 107 108 if start is None or end is None: 109 print("Failed to test_data boundaries from output.") 110 sys.exit(1) 111 112 # Parse plugin output 113 preads, pwrites, seen_all = parse_plugin_output(args.plugin_output, 114 start, end) 115 116 if not seen_all: 117 print("Fail: didn't instrument all accesses to test_data.") 118 sys.exit(1) 119 120 # Compare and report 121 if preads == exp_reads and pwrites == exp_writes: 122 sys.exit(0) 123 else: 124 print("Fail: The memory reads and writes count does not match.") 125 print(f"Expected Reads: {exp_reads}, Actual Reads: {preads}") 126 print(f"Expected Writes: {exp_writes}, Actual Writes: {pwrites}") 127 sys.exit(1) 128 129if __name__ == "__main__": 130 main() 131