blob: 2050478e3f1a682f777127cd2fc62ed8bd7aaade [file] [edit]
#!/bin/sh
# Validate CPU and domain pairing in perf sched stats snapshots (no_mlock)
# SPDX-License-Identifier: GPL-2.0
set -e
# shellcheck source=lib/setup_python.sh
. "$(dirname "$0")/lib/setup_python.sh"
if ! perf version --build-options | grep -q 'libtraceevent:.*on'; then
echo "[Skip] perf sched requires libtraceevent"
exit 2
fi
$PYTHON - <<'PY'
import os
import re
import struct
import subprocess
import sys
import tempfile
# Native-endian perf.data with only NRCPUS and CPU_DOMAIN_INFO features.
endian = '<' if sys.byteorder == 'little' else '>'
def pack(fmt, *values):
return struct.pack(endian + fmt, *values)
def string(value):
data = value.encode() + b'\0'
return pack('I', len(data)) + data
def cpu(cpu_id, timestamp, value, version):
return pack('IHHQIHH6I3Q', 85, 0, 72, timestamp, cpu_id, version, 0,
*([value] * 9))
def domain(cpu_id, domain_id, timestamp, value, version):
# All supported versions use the largest union member's record size.
return pack('IHHQIHH45I4x', 86, 0, 208, timestamp, cpu_id, version,
domain_id, *([value] * 45))
def snapshot(timestamp, value, version=17, cpus=(0, 1, 2), domains=(0, 1)):
records = []
for cpu_id in cpus:
records.append(cpu(cpu_id, timestamp, value + cpu_id * 100, version))
for domain_id in domains:
records.append(domain(cpu_id, domain_id, timestamp,
value + cpu_id * 100, version))
return records
def write_file(path, records, version=17):
metadata = pack('II', version, 2)
for cpu_id in range(3):
metadata += pack('II', cpu_id, 2)
for domain_id in range(2):
metadata += pack('I', domain_id)
if version >= 17:
metadata += string('SMT' if domain_id == 0 else 'MC')
metadata += string('7') + string('0-2')
features = [pack('II', 3, 3), metadata]
data = b''.join(records)
offset = 104 + len(data) + 16 * len(features)
sections = b''
for feature in features:
sections += pack('QQ', offset, len(feature))
offset += len(feature)
header = pack('13Q', 0x32454c4946524550, 104, 144, 104, 0,
104, len(data), 0, 0, (1 << 7) | (1 << 32), 0, 0, 0)
with open(path, 'wb') as output:
output.write(header + data + sections + b''.join(features))
def run(args, valid, domains=True):
result = subprocess.run(['perf', 'sched', 'stats'] + args,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
text=True, timeout=10)
if valid:
assert result.returncode == 0, result.stderr
assert re.search(r'^yld_count\s+:\s+10\b', result.stdout, re.M), result.stdout
if domains:
assert re.search(r'^busy_lb_count\s+:\s+10\b', result.stdout, re.M), result.stdout
else:
assert 'busy_lb_count' not in result.stdout, result.stdout
else:
assert result.returncode > 0, (args, result.returncode, result.stdout)
assert 'Incompatible or incomplete schedstat snapshots' in result.stderr
assert not result.stdout, result.stdout
assert 'Sanitizer' not in result.stderr, result.stderr
with tempfile.TemporaryDirectory(prefix='perf-schedstat-') as directory:
good = os.path.join(directory, 'good.data')
test = os.path.join(directory, 'test.data')
before = snapshot(100, 1000)
after = snapshot(200, 1010)
write_file(good, before + after)
for version in (15, 16, 17):
for timestamp in (100, 200):
write_file(test, snapshot(100, 1000, version) +
snapshot(timestamp, 1010, version), version)
run(['report', '-C', '0,1,2', '-i', test], True)
run(['diff', test, test], True)
print('Matching snapshots, including equal timestamps: [Success]')
write_file(test, snapshot(100, 1000, domains=()) +
snapshot(200, 1010, domains=()))
run(['report', '-C', '0,1,2', '-i', test], True, domains=False)
run(['diff', test, test], True, domains=False)
print('CPUs without domains: [Success]')
write_file(test, before + snapshot(200, 1010, cpus=(0, 1)))
run(['report', '-C', '0,1', '-i', test], True)
run(['report', '-C', '1', '-i', good], True)
write_file(test, snapshot(100, 1000, cpus=(0, 1)) +
snapshot(200, 1010, cpus=(0, 1)))
run(['diff', good, test], True)
print('CPU filtering and different CPU sets across files: [Success]')
write_file(test, snapshot(100, 0xfffffffa, cpus=(0,)) +
snapshot(200, 4, cpus=(0,)))
run(['report', '-C', '0', '-i', test], True)
print('Wrapping 32-bit counters: [Success]')
cases = {
'missing first CPU': before + snapshot(200, 1010, cpus=(1, 2)),
'missing middle CPU': before + snapshot(200, 1010, cpus=(0, 2)),
'missing last CPU': before + snapshot(200, 1010, cpus=(0, 1)),
'added CPU': snapshot(100, 1000, cpus=(0, 1)) + after,
'reordered CPUs': before + snapshot(200, 1010, cpus=(0, 2, 1)),
'equal timestamp, missing first CPU': before + snapshot(100, 1010, cpus=(1, 2)),
'missing first domain': before + snapshot(200, 1010, domains=(1,)),
'missing last domain': before + after[:-1],
'added domain': snapshot(100, 1000, domains=(0,)) + after,
'reordered domains': before + snapshot(200, 1010, domains=(1, 0)),
'domain without CPU': before[1:] + after,
'wrong domain CPU': before + [after[0], domain(1, 0, 200, 1010, 17)] + after[2:],
'changed version': before + snapshot(200, 1010, version=16),
'backwards timestamp': before + snapshot(50, 1010),
'third snapshot': before + after + snapshot(300, 1020),
'missing second snapshot': before,
}
for name, records in cases.items():
write_file(test, records)
run(['report', '-C', '0,1,2', '-i', test], False)
run(['diff', test, good], False)
run(['diff', good, test], False)
print(name + ': [Success]')
PY