You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

76 lines
3.3 KiB

#!/usr/bin/env python3
"""Extract stack and heap address ranges from logfiles"""
from argparse import ArgumentParser
from pathlib import Path
from typing import Dict, List, Optional
import json
import sys
def dump_to_file(data: Dict[str, Dict[str, str]], path: Path) -> None:
"""Dump dict as JSON to file"""
with open(path, "w") as fd:
json.dump(data, fd)
def _range_to_dict(d: Dict[str, str]) -> Dict[str, Dict[str, str]]:
"""Turn stack/heap range into dict with 'start' and 'end' keys"""
return {k : dict(zip(('start', 'end'), v.split(" - "))) for (k, v) in d.items()}
def _overapproximate(ld: List[Dict[str, Dict[str, str]]]) -> Dict[str, Dict[str, str]]:
"""extract lowest start and highest end address"""
acc = lambda f, k, t: f([d[t][k] for d in ld if t in d])
return {k : {'start' : acc(min, 'start', k), 'end' : acc(max, 'end', k)} for k in ld[0].keys()}
def _flatten(d: Dict[str, Dict[str, str]]) -> Dict[str, int]:
"""Flatten to one laye by concatting keys"""
return {f"{k.lower()}_{kk}": int(vv, 16) for (k,v) in d.items() for (kk, vv) in v.items()}
def parse_logfile(logfile: Path) -> Dict[str, Dict[str, str]]:
"""Parse Stack and Heap start + end address from specified logfile"""
with open(logfile, 'r') as fd:
ranges: Dict[str, str] = dict(l.strip().lstrip("[*] ").split(": ") \
for l in fd.readlines() if l.strip() and ("Stack" in l or "Heap" in l))
address_dict = _range_to_dict(ranges)
# Check we didn't mess up the range's order
for k, d in address_dict.items():
assert int(d['start'], 16) < int(d['end'], 16), f"[{k}] Start address {d['start']} > end address {d['end']}"
return address_dict
def extract_stack_and_heap_address(trace_dir: Path, eval_dir: Optional[Path], exact: bool = False) -> None:
"""Extract stack and heap address ranges from some logfile"""
logfiles = list(trace_dir.glob("./*"))
address_dicts = list(map(parse_logfile, logfiles))
if exact:
unique_address_dicts = set(map(str, map(parse_logfile, logfiles)))
assert len(unique_address_dicts) == 1, f"Found {len(unique_address_dicts)} unique address ranges overall logfiles (should be 1)"
address_dict = _flatten(_overapproximate(list(address_dicts)))
print(json.dumps(address_dict))
if not eval_dir is None:
print(f"Dumping to {eval_dir / 'addresses.json'}")
dump_to_file(address_dict, eval_dir / 'addresses.json')
if __name__ == '__main__':
parser = ArgumentParser(description='Extract stack and heap address ranges from logfiles') # pylint: disable=invalid-name
parser.add_argument('trace_dir', nargs=1, help="path to traces directory")
parser.add_argument('--eval_dir', nargs=1, action='store', default=[], help="path to evaluation directory")
parser.add_argument('--exact', action="store_true", default=False, help="Guarantee that address range is the same for all logfiles")
cargs = parser.parse_args() # pylint: disable=invalid-name
trace_dir = (Path(cargs.trace_dir[0]) / "logs").resolve() # pylint: disable=invalid-name
if not trace_dir.exists():
print(f"Trace dir {trace_dir} does not exist. Aborting..")
sys.exit(1)
eval_dir = None
if cargs.eval_dir:
eval_dir = Path(cargs.eval_dir[0]).resolve()
extract_stack_and_heap_address(trace_dir, eval_dir, cargs.exact)