blob: 5634bceeb4331a97b24ff3fc8c558d495d6f3d40 [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2017-2020 The Project X-Ray Authors.
#
# Use of this source code is governed by a ISC-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/ISC
#
# SPDX-License-Identifier: ISC
import os, re
from prjxray import util
def noprefix(tag):
n = tag.find('.')
assert n > 0
return tag[n + 1:]
def getprefix(tag):
n = tag.find('.')
assert n > 0
return tag[0:n]
def load_pipfile(pipfile, verbose=False):
'''Returns a set of tags containing real tile type prefixes (ex: INT_L)'''
todos = set()
tile_type = None
with open(pipfile, "r") as f:
# INT_L.WW2BEG0.SR1BEG_S0
for line in f:
tag = line.strip().split(' ')[0]
prefix_line = getprefix(tag)
if tile_type is None:
tile_type = prefix_line
else:
assert tile_type == prefix_line
todos.add(tag)
return todos, tile_type
def balance_todo_list(
pipfile,
todos,
balance_wire_re,
balance_wire_direction,
balance_wire_cnt,
verbose=False):
"""Balance the contents of the todo list
Todo list balancing allows to specify the name, direction and minimal number of
occurrences of a PIP wire in the final todo list.
The mechanism should be used in cases where a fuzzer times out because of an
unsolvable todo list, i.e. the netlist and resulting segdata generated from an
iteration keep segmatch from properly disambiguating the bits for some features.
When the balance wire name regexp is specified it's guaranteed that all PIPs
with matching wire name (whether we want to match a src or dst wire has to be
specified with the --balance-wire-direction switch) will have at least the number
of entries specified with the --balance-wire-cnt switch in the final todo list.
"""
orig_todos, tile_type = load_pipfile(pipfile, verbose=verbose)
if balance_wire_re is not None:
todo_wires = {}
verbose and print("Start balancing the TODO list")
for todo in todos:
tile_type, dst, src = todo.split(".")
wire = src
other_wire = dst
if balance_wire_direction not in "src":
wire = dst
other_wire = src
balance_wire_match = re.match(balance_wire_re, wire)
if balance_wire_match is None:
continue
if wire not in todo_wires:
todo_wires[wire] = set()
todo_wires[wire].add(other_wire)
for wire, other_wires in todo_wires.items():
if len(other_wires) >= balance_wire_cnt:
continue
else:
for todo in orig_todos:
tile_type, dst, src = todo.split(".")
if balance_wire_direction in "src":
if wire in src and dst not in todo_wires[wire]:
todo_wires[wire].add(dst)
else:
if wire in dst and src not in todo_wires[wire]:
todo_wires[wire].add(src)
if len(todo_wires[wire]) == balance_wire_cnt:
break
for wire, other_wires in todo_wires.items():
if len(other_wires) < balance_wire_cnt:
verbose and print(
"Warning: failed to balance the todo list for wire {}, there are only {} PIPs which meet the requirement: {}"
.format(wire, len(other_wires), other_wires))
for other_wire in other_wires:
line = tile_type + "."
if balance_wire_direction in "src":
line += other_wire + "." + wire
else:
line += wire + "." + other_wire
verbose and print("Adding {}".format(line))
todos.add(line)
verbose and print("Finished balancing the TODO list")
def maketodo(
pipfile,
dbfile,
intre,
exclude_re=None,
balance_wire_re=None,
balance_wire_direction=None,
balance_wire_cnt=None,
not_endswith=None,
verbose=False):
'''
db files start with INT., but pipfile lines start with INT_L
Normalize by removing before the first dot
050-intpips doesn't care about contents, but most fuzzers use the tile type prefix
'''
todos, tile_type = load_pipfile(pipfile, verbose=verbose)
verbose and print('%s: %u entries' % (pipfile, len(todos)))
if not todos:
verbose and print('%s: %u entries, done!' % (pipfile, len(todos)))
return
verbose and print("pipfile todo sample: %s" % list(todos)[0])
if 0 and verbose:
print("TODOs")
for todo in sorted(list(todos)):
print(' %s' % todo)
verbose and print('Pre db %s: %u entries' % (dbfile, len(todos)))
# Allow against empty db
if os.path.exists(dbfile):
verbose and print("Loading %s" % dbfile)
with open(dbfile, "r") as f:
# INT.BYP_ALT0.BYP_BOUNCE_N3_3 !22_07 !23_07 !25_07 21_07 24_07
for line in f:
tag, _bits, mode, _ = util.parse_db_line(line.strip())
# Only count resolved entries
if mode:
continue
# INT.BLAH => INT_L.BLAH
tag = tile_type + '.' + noprefix(tag)
# bipips works on a subset
if tag in todos:
todos.remove(tag)
else:
verbose and print(
"WARNING: couldnt remove %s (line %s)" %
(tag, line.strip()))
else:
verbose and print("WARNING: dbfile doesnt exist: %s" % dbfile)
verbose and print('Post db %s: %u entries' % (dbfile, len(todos)))
drops = 0
lines = 0
filtered_todos = set()
for line in todos:
include = re.match(intre, line) is not None
if include and not_endswith is not None:
include = not line.endswith(not_endswith)
if include and exclude_re is not None:
include = re.match(exclude_re, line) is None
if include:
filtered_todos.add(line)
else:
drops += 1
lines += 1
verbose and print('Print %u entries w/ %u drops' % (lines, drops))
balance_todo_list(
pipfile, filtered_todos, balance_wire_re, balance_wire_direction,
balance_wire_cnt, verbose)
for todo in filtered_todos:
print(todo)
def run(
build_dir,
db_dir,
pip_dir,
intre,
sides,
l,
r,
pip_type,
seg_type,
exclude_re=None,
balance_wire_re=None,
balance_wire_direction=None,
balance_wire_cnt=None,
not_endswith=None,
verbose=False):
if db_dir is None:
db_dir = "%s/%s" % (
os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"))
if pip_dir is None:
pip_dir = "%s/piplist/build/%s" % (
os.getenv("XRAY_FUZZERS_DIR"), pip_type)
assert intre, "RE is required"
for side in sides:
if side == "l" and not l:
continue
if side == "r" and not r:
continue
if side == "xl":
segfile = "l{}".format(seg_type)
pipfile = "l{}".format(pip_type)
elif side == "xr":
segfile = "r{}".format(seg_type)
pipfile = "r{}".format(pip_type)
elif side != "":
segfile = "{}_{}".format(seg_type, side)
pipfile = "{}_{}".format(pip_type, side)
else:
segfile = "{}".format(seg_type)
pipfile = "{}".format(pip_type)
maketodo(
"%s/%s.txt" % (pip_dir, pipfile),
"%s/segbits_%s.db" % (db_dir, segfile),
intre,
exclude_re=exclude_re,
balance_wire_re=balance_wire_re,
balance_wire_direction=balance_wire_direction,
balance_wire_cnt=balance_wire_cnt,
not_endswith=not_endswith,
verbose=verbose)
def main():
import argparse
parser = argparse.ArgumentParser(
description="Print list of known but unsolved PIPs")
parser.add_argument('--verbose', action='store_true', help='')
parser.add_argument('--build-dir', default="build", help='')
parser.add_argument('--db-dir', default=None, help='')
parser.add_argument('--pip-dir', default=None, help='')
parser.add_argument('--re', required=True, help='')
parser.add_argument('--exclude-re', required=False, default=None, help='')
parser.add_argument(
'--balance-wire-re', required=False, default=None, help='')
parser.add_argument(
'--balance-wire-direction', required=False, default="src", help='')
parser.add_argument(
'--balance-wire-cnt', required=False, default="1", help='')
parser.add_argument(
'--balance-re-wire', required=False, default="src", help='')
parser.add_argument('--pip-type', default="pips_int", help='')
parser.add_argument('--seg-type', default="int", help='')
parser.add_argument('--sides', default="l,r", help='')
util.add_bool_arg(parser, '--l', default=True, help='')
util.add_bool_arg(parser, '--r', default=True, help='')
parser.add_argument(
'--not-endswith', help='Drop lines if they end with this')
args = parser.parse_args()
run(
build_dir=args.build_dir,
db_dir=args.db_dir,
pip_dir=args.pip_dir,
intre=args.re,
exclude_re=args.exclude_re,
balance_wire_re=args.balance_wire_re,
balance_wire_direction=args.balance_wire_direction,
balance_wire_cnt=int(args.balance_wire_cnt),
sides=args.sides.split(','),
l=args.l,
r=args.r,
pip_type=args.pip_type,
seg_type=args.seg_type,
not_endswith=args.not_endswith,
verbose=args.verbose)
if __name__ == '__main__':
main()