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