xref: /src/tools/test/hwpmc/pmctest.py (revision bc9229035c5f46674cf06b48d66e9f039b3a9875)
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