gcc/contrib/gen_autofdo_event.py
Andi Kleen 950fa8552b Update perf auto profile script
- Fix gen_autofdo_event: The download URL for the Intel Perfmon Event
  list has changed, as well as the JSON format.
  Also it now uses pattern matching to match CPUs. Update the script to support all of this.
- Regenerate gcc-auto-profile with the latest published Intel model
  numbers, so it works with recent systems.
- So far it's still broken on hybrid systems

contrib/ChangeLog:

	* gen_autofdo_event.py: Update for download server changes

gcc/ChangeLog

	* config/i386/gcc-auto-profile: Regenerate.
2023-06-12 19:22:22 -07:00

177 lines
5.5 KiB
Python
Executable File

#!/usr/bin/python3
# Generate Intel taken branches Linux perf event script for autofdo profiling.
# Copyright (C) 2016-2023 Free Software Foundation, Inc.
#
# GCC is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 3, or (at your option) any later
# version.
#
# GCC is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with GCC; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>. */
# Run it with perf record -b -e EVENT program ...
# The Linux Kernel needs to support the PMU of the current CPU, and
# It will likely not work in VMs.
# Add --all to print for all cpus, otherwise for current cpu.
# Add --script to generate shell script to run correct event.
#
# Requires internet (https) access. This may require setting up a proxy
# with export https_proxy=...
#
import urllib.request
import sys
import json
import argparse
import collections
import os
import fnmatch
baseurl = "https://raw.githubusercontent.com/intel/perfmon/main"
target_events = ('BR_INST_RETIRED.NEAR_TAKEN',
'BR_INST_EXEC.TAKEN',
'BR_INST_RETIRED.TAKEN_JCC',
'BR_INST_TYPE_RETIRED.COND_TAKEN')
ap = argparse.ArgumentParser()
ap.add_argument('--all', '-a', help='Print for all CPUs', action='store_true')
ap.add_argument('--script', help='Generate shell script', action='store_true')
args = ap.parse_args()
eventmap = collections.defaultdict(list)
def get_cpustr():
cpuinfo = os.getenv("CPUINFO")
if cpuinfo is None:
cpuinfo = '/proc/cpuinfo'
f = open(cpuinfo, 'r')
cpu = [None, None, None, None]
for j in f:
n = j.split()
if n[0] == 'vendor_id':
cpu[0] = n[2]
elif n[0] == 'model' and n[1] == ':':
cpu[2] = int(n[2])
elif n[0] == 'cpu' and n[1] == 'family':
cpu[1] = int(n[3])
elif n[0] == 'stepping' and n[1] == ':':
cpu[3] = int(n[2])
if all(v is not None for v in cpu):
break
# stepping for SKX only
stepping = cpu[0] == "GenuineIntel" and cpu[1] == 6 and cpu[2] == 0x55
if stepping:
return "%s-%d-%X-%X" % tuple(cpu)
return "%s-%d-%X" % tuple(cpu)[:3]
def find_event(eventurl, model):
print("Downloading", eventurl, file = sys.stderr)
u = urllib.request.urlopen(eventurl)
events = json.loads(u.read())["Events"]
u.close()
found = 0
for j in events:
if j['EventName'] in target_events:
event = "cpu/event=%s,umask=%s/" % (j['EventCode'], j['UMask'])
if 'PEBS' in j and int(j['PEBS']) > 0:
event += "p"
if args.script:
eventmap[event].append(model)
else:
print(j['EventName'], "event for model", model, "is", event)
found += 1
return found
if not args.all:
cpu = get_cpustr()
if not cpu:
sys.exit("Unknown CPU type")
url = baseurl + "/mapfile.csv"
print("Downloading", url, file = sys.stderr)
u = urllib.request.urlopen(url)
found = 0
cpufound = 0
for j in u:
n = j.rstrip().decode().split(',')
if len(n) >= 4 and (args.all or fnmatch.fnmatch(cpu, n[0])) and n[3] == "core":
components = n[0].split("-")
model = components[2]
model = int(model, 16)
cpufound += 1
found += find_event(baseurl + n[2], model)
u.close()
if args.script:
print('''#!/bin/sh
# Profile workload for gcc profile feedback (autofdo) using Linux perf.
# Auto generated. To regenerate for new CPUs run
# contrib/gen_autofdo_event.py --script --all in gcc source
# usages:
# gcc-auto-profile program (profile program and children)
# gcc-auto-profile -a sleep X (profile all for X secs, may need root)
# gcc-auto-profile -p PID sleep X (profile PID)
# gcc-auto-profile --kernel -a sleep X (profile kernel)
# gcc-auto-profile --all -a sleep X (profile kernel and user space)
# Identify branches taken event for CPU.
#
FLAGS=u
if [ "$1" = "--kernel" ] ; then
FLAGS=k
shift
fi
if [ "$1" = "--all" ] ; then
FLAGS=uk
shift
fi
if ! grep -q Intel /proc/cpuinfo ; then
echo >&2 "Only Intel CPUs supported"
exit 1
fi
if grep -q hypervisor /proc/cpuinfo ; then
echo >&2 "Warning: branch profiling may not be functional in VMs"
fi
case `grep -E -q "^cpu family\s*: 6" /proc/cpuinfo &&
grep -E "^model\s*:" /proc/cpuinfo | head -n1` in''')
for event, mod in eventmap.items():
for m in mod[:-1]:
print("model*:\ %s|\\" % m)
print('model*:\ %s) E="%s$FLAGS" ;;' % (mod[-1], event))
print('''*)
echo >&2 "Unknown CPU. Run contrib/gen_autofdo_event.py --all --script to update script."
exit 1 ;;''')
print("esac")
print("set -x")
print('if ! perf record -e $E -b "$@" ; then')
print(' # PEBS may not actually be working even if the processor supports it')
print(' # (e.g., in a virtual machine). Trying to run without /p.')
print(' set +x')
print(' echo >&2 "Retrying without /p."')
print(' E="$(echo "${E}" | sed -e \'s/\/p/\//\')"')
print(' set -x')
print(' exec perf record -e $E -b "$@"')
print(' set +x')
print('fi')
if cpufound == 0 and not args.all:
sys.exit('CPU %s not found' % cpu)
if found == 0:
sys.exit('Branch event not found')