Add pulptrace v0.1.1 script

This commit is contained in:
bluew 2020-11-16 16:39:02 +01:00
parent 012fd1be12
commit fbf2e108c0

193
scripts/pulptrace Executable file
View file

@ -0,0 +1,193 @@
#!/usr/bin/env python3
# Copyright 2019 ETH Zurich and University of Bologna
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Author: Robert Balas (balasr@iis.ee.ethz.ch)
# History
# 0.1.0:
# - initial release
# 0.1.1:
# - Add dot to allowable character expression detecting symbolic address
import argparse
import sys
import re
import os
import subprocess
from functools import reduce
from collections import OrderedDict
parser = argparse.ArgumentParser(prog='pulptrace',
description="""Combine objdump information
with an instruction trace log from a pulp
core""")
parser.version = '0.1.1'
parser.add_argument('trace_file', type=str, help='trace log from a pulp core')
parser.add_argument('elf_file', type=str,
help='elf file that was ran, producing the trace log')
parser.add_argument('-e,', '--objdump', type=str, help="""use the provided objdump
executable""")
parser.add_argument('-o,', '--output', type=str, help="""write to file instead of
stdout""")
parser.add_argument('-t', '--truncate', action='store_true',
help='truncate overlong text')
parser.add_argument('-n', '--numeric', action='store_true',
help='show numeric register names')
parser.add_argument('--no-aliases', action='store_true',
help='do not use aliases for instruction names')
parser.add_argument('--cycles', action='store_true',
help='show cycle count extracted from log')
parser.add_argument('--time', action='store_true',
help='show passed time extracted from log')
args = parser.parse_args()
trace_filename = args.trace_file
elf_filename = args.elf_file
regs_map = [("x0", "zero"), ("x1", "ra"), ("x2", "sp"),
("x3", "gp"), ("x4", "tp"), ("x5", "t0"),
("x6", "t1"), ("x7", "t2"), ("x8", "s0"),
("x9", "s1"), ("x10", "a0"), ("x11", "a1"),
("x12", "a2"), ("x13", "a3"), ("x14", "a4"),
("x15", "a5"), ("x16", "a6"), ("x17", "a7"),
("x18", "s2"), ("x19", "s3"), ("x20", "s4"),
("x21", "s5"), ("x22", "s6"), ("x23", "s7"),
("x24", "s8"), ("x25", "s9"), ("x26", "s10"),
("x27", "s11"), ("x28", "t3"), ("x29", "t4"),
("x30", "t5"), ("x31", "t6")]
# augment regs_map with prefixes/postfixes to prevent false positives
# ugly, but works
tmp_regs_map = []
for item in regs_map:
k, v = item
tmp_regs_map.append([' ' + k, ' ' + v])
tmp_regs_map.append(['(' + k, '(' + v])
tmp_regs_map.append([k + ':', v + ':'])
tmp_regs_map.append([k + '=', v + '='])
regs_map = tmp_regs_map
# we want to replace the higher numbered register first with their alias,
# otherwise x31 could be replaced to gp1
regs_alias = OrderedDict(reversed(regs_map))
objdump_insns = dict()
# parse objdump output and generate hashmap of address and insn string
objdump_bin = ''
if args.objdump:
objdump_bin = args.objdump
elif os.getenv('RISCV'):
objdump_bin = os.getenv('RISCV') + '/bin/' + 'riscv32-unknown-elf-objdump'
else:
objdump_bin = 'riscv32-unknown-elf-objdump'
with subprocess.Popen([objdump_bin, "--prefix-addresses"]
+ (['-Mnumeric'] if args.numeric else [])
+ (['-Mno-aliases'] if args.no_aliases else [])
+ ["-d", elf_filename],
stdout=subprocess.PIPE) as proc:
for line in proc.stdout:
line = line.decode("ascii")
if line == '':
break
match = re.match(r'^\s*([0-9a-f]+)\s+(<[0-9a-zA-Z+_.]*>)\s+(.*)', line)
if match:
# group(1) = instruction address
# group(2) = instruction address symbolic
# group(3) = instruction name
objdump_insns[int(match.group(1), 16)] = (
match.group(2), match.group(3).replace("\t", " "))
def truncate_string(string, length):
return string[:length-2] + (string[length-2:] and '..')
# redirect to stdout to file if desired
sys.stdout = open(args.output, "w") if args.output else sys.stdout
with open(trace_filename, "r") as f:
pc = 0
last_irq = False
# skip trace file "header"
f.readline()
# parse instructions
for line in f:
insn_line = line.split()
time = insn_line[0]
cycles = insn_line[1]
addr = insn_line[2]
# insn_bytes = insn_line[3]
# insn_str = insn_line[4]
# insn_rest = insn_line[5::]
reg_vals = ""
insn_only = ""
# this is a dirty heuristic which figures out if we have register
# values in the trace file TODO: improve
bound = 80
if len(line) > bound:
reg_vals = line[bound:].strip()
insn_only = line[:bound-1].strip()
insn_addr = int(addr.replace("x", "0"), 16)
if not(args.numeric):
# TODO: this might not be reliable if we have values like x10 in
# the registers
reg_vals = reduce(lambda a, kv: a.replace(*kv),
regs_alias.items(), reg_vals)
if args.time:
print('%-12s ' % time, end='')
if args.cycles:
print('%-12d ' % (int(cycles)), end='')
if insn_addr in objdump_insns:
source_location, objdump_insn_str = objdump_insns[insn_addr]
if args.truncate:
source_location = truncate_string(source_location, 40)
objdump_insn_str = truncate_string(objdump_insn_str, 40)
print("%08x: %-40s %-40s %-20s" % (insn_addr,
source_location,
objdump_insn_str,
reg_vals))
else:
match = re.match(r'^\s*[0-9a-f]+[nmu]s\s+[0-9]+\s+[0-9a-f]+\s+[0-9a-f]+\s+(.*)',
insn_only)
insn_str = match.group(1)
if not(args.numeric):
insn_str = reduce(lambda a, kv: a.replace(*kv),
regs_alias.items(), insn_str)
print("%08x: %-40s %-40s %-20s" % (insn_addr,
"", # no objdump info
insn_str, # insn name
reg_vals))
if args.output:
sys.stdout.close()