6da45e7f26
(1) Introduce tracegen.py, a script that invokes the emulator (built with TraceGenConfig), sending a SIGTERM once all cores are finished. (2) Update toaxe.py to gather some statistics about the trace. (3) Introduce tracestats.py, which displays the stats in a useful way. (4) Introduce tracegen+check.py, a top-level script that generates traces, checks them, and emits stats. If this commit is pulled, it should be done after pulling my latest groundtest commit.
204 lines
6.9 KiB
Python
Executable File
204 lines
6.9 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# This file was originally written by Matthew Naylor, University of
|
|
# Cambridge.
|
|
#
|
|
# This software was partly developed by the University of Cambridge
|
|
# Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
|
|
# ("CTSRD"), as part of the DARPA CRASH research programme.
|
|
#
|
|
# This software was partly developed by the University of Cambridge
|
|
# Computer Laboratory under DARPA/AFRL contract FA8750-11-C-0249
|
|
# ("MRC2"), as part of the DARPA MRC research programme.
|
|
#
|
|
# This software was partly developed by the University of Cambridge
|
|
# Computer Laboratory as part of the Rigorous Engineering of
|
|
# Mainstream Systems (REMS) project, funded by EPSRC grant
|
|
# EP/K008528/1.
|
|
|
|
# -------
|
|
# Outline
|
|
# -------
|
|
|
|
# This script takes memory-subsystem traces produced by the groundtest
|
|
# trace generator (see tracegen.scala) and puts them into a format
|
|
# that can be validated by the axe tool (see
|
|
# https://github.com/CTSRD-CHERI/axe).
|
|
|
|
import sys
|
|
import re
|
|
import sets
|
|
|
|
def main():
|
|
|
|
if len(sys.argv) < 2:
|
|
sys.stderr.write("Usage: toaxe.py TRACE-FILE [STATS-OUT-FILE]\n")
|
|
sys.exit(-1)
|
|
|
|
if sys.argv[1] == "-":
|
|
f = sys.stdin
|
|
else:
|
|
f = open(sys.argv[1], 'r')
|
|
if f == None:
|
|
sys.stderr.write("File not found: " + sys.argv[1] + "\n")
|
|
sys.exit(-1)
|
|
|
|
statsFile = None
|
|
if len(sys.argv) > 2:
|
|
statsFile = open(sys.argv[2], 'a')
|
|
|
|
lineCount = 0
|
|
def error(msg):
|
|
sys.stderr.write("Error at line " + str(lineCount) + ": " + msg + "\n")
|
|
sys.exit(-1)
|
|
|
|
# Mapping from address to axe address
|
|
addrMap = {}
|
|
nextAddr = 0
|
|
|
|
# Mapping from (thread id, tag id) to axe operation id
|
|
tagMap = {}
|
|
|
|
# Mapping from thread id to operation id
|
|
fenceReq = {}
|
|
|
|
# Mapping from thread id to operation id
|
|
loadReserve = {}
|
|
|
|
# Array of axe operations
|
|
ops = []
|
|
|
|
# Statistics
|
|
scCount = 0 # Number of store-conditionals
|
|
scSuccessCount = 0 # Number of store-conditionals that succeeded
|
|
loadCount = 0 # Number of loads
|
|
loadExtCount = 0 # Number of loads of value written by another core
|
|
|
|
# The previous write to each address by each thread
|
|
prevWrite = {}
|
|
|
|
for line in f:
|
|
# Parse thread id and command
|
|
m = re.search(' *([0-9]+) *: *([^ ]*) (.*)', line)
|
|
if m == None: error("Expected: <thread-id> ':' <command>")
|
|
tid, cmd, line = m.group(1), m.group(2), m.group(3)
|
|
|
|
if cmd == 'fence-req':
|
|
# Parse time
|
|
m = re.search(' *@ *([0-9]+)', line)
|
|
if m == None: error ("expected timestamp")
|
|
# Insert placeholder containing request time
|
|
ops.append(str(m.group(1)))
|
|
fenceReq[tid] = len(ops)-1
|
|
elif cmd == 'fence-resp':
|
|
# Insert 'sync' operation
|
|
if not (tid in fenceReq) or fenceReq[tid] == None:
|
|
error("fence-resp without fence-req on thread " + tid)
|
|
startTime = ops[fenceReq[tid]]
|
|
op = str(tid) + ": sync @ " + startTime
|
|
# Add end-time
|
|
m = re.search(' *@ *([0-9]+)', line)
|
|
if m != None: op = op + ":" + str(m.group(1))
|
|
ops[fenceReq[tid]] = (op,)
|
|
fenceReq[tid] = None
|
|
elif cmd == 'load-req' or cmd == 'load-reserve-req':
|
|
# Parse address, tag, and time
|
|
m = re.search(' *([0-9a-fx]+) *# *([0-9]+) *@ *([0-9]+)', line)
|
|
if m == None: error("expected <address> #<tag> @<timestamp>")
|
|
# Update address map
|
|
if not (m.group(1) in addrMap):
|
|
addrMap[m.group(1)] = nextAddr
|
|
nextAddr = nextAddr+1
|
|
# Insert place-holder
|
|
ops.append((cmd, None, addrMap[m.group(1)], m.group(3), None))
|
|
tagMap[(tid, m.group(2))] = len(ops)-1
|
|
if cmd == 'load-reserve-req':
|
|
loadReserve[tid] = len(ops)-1
|
|
elif cmd == 'store-req' or cmd == 'store-cond-req' or cmd == 'swap-req':
|
|
# Parse value, address, tag, and time
|
|
m = re.search(' *([0-9]+) *([0-9a-fx]+) *# *([0-9]+) *@ *([0-9]+)', line)
|
|
if m == None: error("expected <value> <address> #<tag> @<timestamp>")
|
|
# Update address map
|
|
if not (m.group(2) in addrMap):
|
|
addrMap[m.group(2)] = nextAddr
|
|
nextAddr = nextAddr+1
|
|
# Insert place-holder
|
|
lr = loadReserve[tid] if tid in loadReserve else None
|
|
ops.append((cmd, m.group(1), addrMap[m.group(2)], m.group(4), lr))
|
|
tagMap[(tid, m.group(3))] = len(ops)-1
|
|
if cmd == 'store-cond-req': loadReserve[tid] = None
|
|
prevWrite[(tid, addrMap[m.group(2)])] = m.group(1)
|
|
elif cmd == 'resp':
|
|
# Parse value and timestamp
|
|
m = re.search(' *([0-9]+) *# *([0-9]+) *@ *([0-9]+)', line)
|
|
if m == None: error("expected <value> #<tag> @<timestamp>")
|
|
# Find corresponding response
|
|
tag = m.group(2)
|
|
if not ((tid, tag) in tagMap) or tagMap[(tid, tag)] == None:
|
|
error("resp without associated req with tag " + tag +
|
|
" on thread " + tid)
|
|
opId = tagMap[(tid, tag)]
|
|
(c, val, addr, start, lr) = ops[opId]
|
|
if c == 'load-req' or c == 'load-reserve-req':
|
|
loadCount = loadCount+1
|
|
if prevWrite.get((tid, addr), None) != m.group(1):
|
|
loadExtCount = loadExtCount+1
|
|
if c == 'load-req':
|
|
op = tid + ": M[" + str(addr) + '] == ' + m.group(1) + ' @ '
|
|
op += start + ':' + m.group(3)
|
|
ops[opId] = (op,)
|
|
elif c == 'store-req':
|
|
op = tid + ": M[" + str(addr) + '] := ' + val + ' @ '
|
|
op += start + ':' # + m.group(3)
|
|
ops[opId] = (op,)
|
|
elif c == 'load-reserve-req':
|
|
ops[opId] = (m.group(1), start, m.group(3))
|
|
elif c == 'store-cond-req':
|
|
if lr == None: error("store conditional without load-reserve")
|
|
(loadVal, loadStart, loadFin) = ops[lr]
|
|
scCount = scCount + 1
|
|
if int(m.group(1)) != 0:
|
|
# SC fail
|
|
op = tid + ": M[" + str(addr) + "] == " + loadVal
|
|
op += " @ " + loadStart + ":" + loadFin
|
|
else:
|
|
# SC success
|
|
scSuccessCount = scSuccessCount + 1
|
|
op = tid + ": { M[" + str(addr) + "] == " + loadVal + "; "
|
|
op += "M[" + str(addr) + "] := " + val + "} @ "
|
|
op += loadStart + ":" + loadFin
|
|
ops[lr] = (op,)
|
|
ops[opId] = None
|
|
elif c == 'swap-req':
|
|
op = tid + ": { M[" + str(addr) + '] == ' + m.group(1)
|
|
op += '; M[' + str(addr) + '] := ' + val
|
|
op += '} @ ' + start + ':' # + m.group(3)
|
|
ops[opId] = (op,)
|
|
else:
|
|
error("Unknown command '" + cmd + "'")
|
|
|
|
lineCount = lineCount+1
|
|
|
|
# Print statistics
|
|
scSuccessRate = str(scSuccessCount/float(scCount))[0:6]
|
|
loadExtRate = str(loadExtCount/float(loadCount))[0:6]
|
|
print("# LRSC_Success_Rate=" + scSuccessRate)
|
|
if statsFile != None:
|
|
statsFile.write("LRSC_Success_Rate=" + scSuccessRate + "\n")
|
|
statsFile.write("Load_External_Rate=" + loadExtRate + "\n")
|
|
statsFile.close()
|
|
|
|
# Print address map in comments
|
|
for addr in addrMap:
|
|
print ("# &M[" + str(addrMap[addr]) + "] == " + addr)
|
|
|
|
# Print axe trace
|
|
for op in ops:
|
|
if op != None and isinstance(op, tuple) and len(op) == 1:
|
|
print op[0]
|
|
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
sys.exit(-1)
|