binding_failure_cold.py (4557B)
1 #!/usr/bin/env python3 2 """One bounded cold pair with retained timeout logs, CPU and receipt progress.""" 3 import argparse,json,os,signal,subprocess,time 4 from pathlib import Path 5 from process_rss import ChildRSS 6 from float_multiply import Runner,sha 7 from binding_substitution import ROOT 8 from rts_metrics import parse_rts_stats 9 10 11 def main(): 12 p=argparse.ArgumentParser(description=__doc__) 13 p.add_argument('tricu',type=Path);p.add_argument('--directory',required=True,type=Path) 14 p.add_argument('--candidate',type=Path,default=Path('/tmp/tricu-binding-compiler-first')) 15 p.add_argument('--failed',type=Path,default=Path('/tmp/tricu-binding-compiler-measure')) 16 args=p.parse_args();root=args.directory.resolve();root.mkdir(parents=True,exist_ok=False) 17 workspace=root/'workspace';workspace.mkdir() 18 previous=json.loads((args.failed/'report.json').read_text()) 19 for name,digest in previous['sources'].items(): 20 origin=args.failed/'workspace'/name;assert sha(origin)==digest 21 (workspace/name).write_bytes(origin.read_bytes()) 22 candidate=json.loads((args.candidate/'report.json').read_text()) 23 assert sha(args.candidate/'candidate.arboricx')==candidate['candidate_sha256'] 24 runner=Runner(args.tricu,root,650,memory_mib=2048) 25 report=dict(complete=False,harness_sha256=sha(Path(__file__)),prior_failure_report_sha256=sha(args.failed/'report.json'), 26 candidate_sha256=candidate['candidate_sha256'],runtime_sha256=sha(args.tricu),attempts=[]) 27 try: 28 for role,bundle in [('candidate',args.candidate/'candidate.arboricx'),('accepted',ROOT/'bootstrap/build-stable.arboricx')]: 29 pin=runner.imported(bundle,'import-'+role) 30 store=root/(role+'-store');output=root/(role+'.arboricx') 31 command=[str(args.tricu),'arboricx','compile','--file',str(workspace/'driver.tri'), 32 '--toolchain',pin,'--tool-store',str(runner.store),'--store',str(store), 33 '--output',str(output),'--compile-timeout','240','+RTS','-N1','-M2048m','-T','-s','-RTS'] 34 row=dict(role=role,command=command,samples=[]);report['attempts'].append(row) 35 with (root/(role+'.log')).open('w') as log: 36 process=subprocess.Popen(command,stdout=log,stderr=subprocess.STDOUT,start_new_session=True) 37 start=time.monotonic();monitor=ChildRSS(process);last=-1 38 try: 39 while process.poll() is None: 40 rss=monitor.sample();elapsed=time.monotonic()-start 41 if rss is not None and rss>2048*1024:raise RuntimeError('RSS guard exceeded') 42 if int(elapsed)>last: 43 last=int(elapsed);cpu=None 44 if monitor.pid: 45 try: 46 fields=Path(f'/proc/{monitor.pid}/stat').read_text().rsplit(')',1)[1].split() 47 cpu=(int(fields[11])+int(fields[12]))/os.sysconf('SC_CLK_TCK') 48 except FileNotFoundError:pass 49 cache=store/'cache/source-build' 50 row['samples'].append(dict(seconds=elapsed,cpu_seconds=cpu,rss_kib=rss, 51 completed=sorted(x.name for x in cache.iterdir()) if cache.exists() else [])) 52 (root/'progress.json').write_text(json.dumps(report,indent=2)+'\n') 53 if elapsed>270:raise RuntimeError('wall-time guard exceeded') 54 time.sleep(.05) 55 row['outcome']='complete' if process.returncode==0 else 'command-failed' 56 except RuntimeError as error:row['outcome']=str(error) 57 finally: 58 if process.poll() is None: 59 os.killpg(process.pid,signal.SIGTERM) 60 try:process.wait(timeout=10) 61 except subprocess.TimeoutExpired:os.killpg(process.pid,signal.SIGKILL);process.wait() 62 row['seconds']=time.monotonic()-start;row['returncode']=process.returncode 63 row['rts']=parse_rts_stats((root/(role+'.log')).read_text()) 64 if output.exists():row['bundle_sha256']=sha(output) 65 print(json.dumps({k:v for k,v in row.items() if k not in ['command','samples']}),flush=True) 66 report['complete']=True 67 finally:(root/'report.json').write_text(json.dumps(report,indent=2)+'\n') 68 69 70 if __name__=='__main__':main()