1#!/usr/bin/env python 2# SPDX-License-Identifier: BSD-3-Clause 3# 4# Copyright (c) 2012, Neville-Neil Consulting 5# Copyright (c) 2026, George V. Neville-Neil 6# All rights reserved. 7# 8 9# Description: A program to run a simple program against every available 10# pmc counter present in a system. 11# 12# To use: 13# 14# pmctest.py -p ls > /dev/null 15# 16# This should result in ls being run with every available counter 17# and the system should neither lock up nor panic. 18# 19# The default is not to wait after each counter is tested. Since the 20# prompt would go to stdout you won't see it, just press return 21# to continue or Ctrl-D to stop. 22 23import sys 24import subprocess 25from subprocess import PIPE 26import argparse 27import tempfile 28from pathlib import Path 29import os 30import shlex 31 32def gather_counters(): 33 """Run program and return output as array of lines.""" 34 result = subprocess.run("pmccontrol -L", shell=True, capture_output=True, text=True) 35 tabbed = result.stdout.strip().split('\n') 36 return [line.replace('\t', '') for line in tabbed] 37 38# A list of strings that are not really counters, just 39# name tags that are output by pmccontrol -L 40notcounter = ["IAF", "IAP", "TSC", "UNC", "UCF", "UCP", "SOFT" ] 41 42def main(): 43 44 parser = argparse.ArgumentParser(description='Exercise a program under hwpmc') 45 parser.add_argument('--program', type=str, required=True, help='target program') 46 parser.add_argument('--wait', action='store_true', help='Wait after each counter.') 47 parser.add_argument('--count', action='store_true', help='Exercise the program being studied using counting mode pmcs.') 48 parser.add_argument('--sample', action='store_true', help='Exercise the program being studied using sampling mode pmcs.') 49 50 args = parser.parse_args() 51 52 counters = gather_counters() 53 54 if len(counters) <= 0: 55 print("no counters found") 56 sys.exit() 57 58 if args.count == True and args.sample == True: 59 print("Choose one of --count OR --sample.") 60 sys.exit() 61 62 # Split program and arguments properly 63 program_parts = shlex.split(args.program) 64 program = Path(program_parts[0]).name 65 66 if args.count == True: 67 tmpdir = tempfile.mkdtemp(prefix=program + "-", suffix="-counting-pmc") 68 print("Exercising program ", args.program, " storing results data in ", tmpdir) 69 70 if args.sample == True: 71 tmpdir = tempfile.mkdtemp(prefix=program + "-", suffix="-sampling-pmc") 72 print("Exercising program ", args.program, " storing results data in ", tmpdir) 73 74 for counter in counters: 75 if counter in notcounter: 76 continue 77 if args.count == True: 78 with open(tmpdir + "/" + program + "-" + counter + ".txt", 'w') as file: 79 p = subprocess.Popen(["pmcstat", "-p", counter] + program_parts, 80 text=True, stderr=file, stdout=file) 81 result = p.wait() 82 print(result) 83 elif args.sample == True: 84 pmcout = tmpdir + "/" + program + "-" + counter + ".pmc" 85 p = subprocess.Popen(["pmcstat", 86 "-O", pmcout, 87 "-P", counter] + program_parts, 88 text=True, stderr=PIPE) 89 result = p.wait() 90 resdir = tmpdir + "/" + program + "-" + counter + ".results" 91 os.makedirs(resdir) 92 p = subprocess.Popen(["pmcstat", 93 "-R", pmcout, 94 "-g"], 95 cwd=resdir, 96 text=True, stderr=PIPE) 97 result = p.wait() 98 gmondir = resdir + "/" + counter 99 if Path(gmondir).is_dir(): 100 with open(gmondir + "/" + "gprof.out", "w") as file: 101 p = subprocess.Popen(["gprof", 102 program_parts[0], 103 program + ".gmon"], 104 cwd=gmondir, 105 text=True, 106 stdout=file, 107 stderr=subprocess.STDOUT) 108 result = p.wait() 109 else: 110 print ("Failed to get gmon data for ", counter) 111 print(result) 112 113 else: 114 p = subprocess.Popen(["pmcstat", "-p", counter] + program_parts, 115 text=True, stderr=PIPE) 116 result = p.wait() 117 print(result) 118 if (args.wait == True): 119 try: 120 value = input("Waitin for you to press ENTER") 121 except EOFError: 122 sys.exit() 123 124# The canonical way to make a python module into a script. 125# Remove if unnecessary. 126 127if __name__ == "__main__": 128 main() 129