132 lines
5.8 KiB
Python
132 lines
5.8 KiB
Python
#!coding=utf8
|
|
|
|
import os
|
|
from .util import *
|
|
import time
|
|
|
|
class BRTParser:
|
|
|
|
def __init__(self, check_trace=True) -> None:
|
|
self.magic_head = b'\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe'
|
|
self.magic_tail = b'\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed'
|
|
self.type_map = {
|
|
101 : "C.J",
|
|
102 : "C.JR",
|
|
103 : "C.CALL",
|
|
104 : "C.RET",
|
|
105 : "C.JALR",
|
|
201 : "P.JAL",
|
|
203 : "P.CALL",
|
|
204 : "P.RET",
|
|
0 : "*.CBR",
|
|
1 : "I.JAL",
|
|
2 : "I.JALR",
|
|
3 : "I.CALL",
|
|
4 : "I.RET",
|
|
}
|
|
self.logger = get_logger(self.__class__.__name__)
|
|
self.check_trace = check_trace
|
|
self.clear()
|
|
|
|
def clear(self):
|
|
self.branchs = {}
|
|
self.branchs_step = []
|
|
self.statistics_type = {}
|
|
|
|
def disable_check(self):
|
|
self.check_trace = False
|
|
|
|
def enable_check(self):
|
|
self.check_trace = True
|
|
|
|
def parse_data(self, data):
|
|
index = int.from_bytes(data[0:8], byteorder='little')
|
|
pc = int.from_bytes(data[8:16], byteorder='little')
|
|
target = int.from_bytes(data[16:24], byteorder='little')
|
|
taken = int.from_bytes(data[24:28], byteorder='little') > 0
|
|
btype = int.from_bytes(data[28:32], byteorder='little')
|
|
if (btype not in self.type_map) and self.check_trace:
|
|
self.logger.warning("Find Unrecognized Type: %d" % btype)
|
|
btype = self.type_map.get(btype, "ERROR-%s"%btype)
|
|
key = pc
|
|
return key, index, pc, target, taken, btype
|
|
|
|
def load(self, file):
|
|
if not os.path.isfile(file):
|
|
self.logger.error("file: %s not find!" % file)
|
|
return
|
|
self.logger.debug("Load file: %s"%file)
|
|
if not self.check_trace:
|
|
self.logger.warning("Trace check is disabled!")
|
|
time_start = time.time()
|
|
with open(file, "rb") as fp:
|
|
# read header
|
|
header = fp.read(16)
|
|
if(header != self.magic_head):
|
|
self.logger.error("file[%s] is not a branch/jump trace")
|
|
return
|
|
pre_pc = -1
|
|
while True:
|
|
data = fp.read(32)
|
|
if(data == self.magic_tail):
|
|
break
|
|
key, index, pc, target, taken, btype = self.parse_data(data)
|
|
if pc < pre_pc and self.check_trace:
|
|
self.logger.warning("Detect disordered PC (0x%x => 0x%x) sequence; potentially indicating a corrupted trace file." % (pre_pc, pc))
|
|
if taken:
|
|
pre_pc = target
|
|
if key not in self.branchs:
|
|
self.branchs[key] = {"pc": pc, "index": [index], "target": [target], "taken": [taken], "type": btype}
|
|
# statistic
|
|
if btype not in self.statistics_type:
|
|
self.statistics_type[btype] = {"count":1, "taken": int(taken), "notaken": int(not taken)}
|
|
else:
|
|
self.statistics_type[btype]["count"] += 1
|
|
self.statistics_type[btype]["taken"] += int(taken)
|
|
self.statistics_type[btype]["notaken"] += int(not taken)
|
|
else:
|
|
self.branchs[key]["index"].append(index)
|
|
self.branchs[key]["target"].append(target)
|
|
self.branchs[key]["taken"].append(taken)
|
|
self.statistics_type[btype]["taken"] += int(taken)
|
|
self.statistics_type[btype]["notaken"] += int(not taken)
|
|
|
|
self.branchs_step.append((index, self.branchs[key], len(self.branchs[key]["index"]) - 1))
|
|
self.logger.debug("%d branchs (%d checks), loaded! time cost: %s"%(len(self.branchs), len(self.branchs_step), fmt_seconds(time.time() - time_start)))
|
|
|
|
def fetch(self, file):
|
|
from . import NemuBR as nbr
|
|
nbr.br_monitor_init(["", "-b", file])
|
|
while True:
|
|
data = nbr.br_monitor_get()
|
|
if data.index < 0:
|
|
return None
|
|
pc, index, target, taken, btype = data.pc, data.index, data.target, data.taken, data.type
|
|
if (btype not in self.type_map) and self.check_trace:
|
|
self.logger.warning("Find Unrecognized Type: %d" % btype)
|
|
btype = self.type_map.get(btype, "ERROR-%s"%btype)
|
|
if btype not in self.statistics_type:
|
|
self.statistics_type[btype] = {"count":1, "taken": int(taken), "notaken": int(not taken)}
|
|
else:
|
|
self.statistics_type[btype]["count"] += 1
|
|
self.statistics_type[btype]["taken"] += int(taken)
|
|
self.statistics_type[btype]["notaken"] += int(not taken)
|
|
yield {"pc": pc, "index": index, "target": target, "taken": taken, "type": btype}
|
|
|
|
def print_stat(self):
|
|
keys = self.statistics_type.keys()
|
|
print("\n%5s %8s %8s %8s %8s" % ("Index", "Type", "icount", "taken", "notaken"))
|
|
count, taken, notaken = 0, 0, 0
|
|
all_cal, all_ret = 0, 0
|
|
for i, k in enumerate(sorted(keys)):
|
|
data = self.statistics_type[k]
|
|
print("%5d %8s %8d %8d %8d" % (i, k, data["count"], data["taken"], data["notaken"]))
|
|
count += data["count"]
|
|
taken += data["taken"]
|
|
notaken += data["notaken"]
|
|
if ".RET" in k:
|
|
all_ret += data["taken"]
|
|
elif ".CALL" in k:
|
|
all_cal += data["taken"]
|
|
print("%5d %8s %8d %8d %8d (%d checks, ins.ret - ins.call = %d)\n" % (len(keys), "ALL", count, taken, notaken, taken + notaken, all_ret - all_cal))
|