swayfx-mosaic/tooling/probes/step20c-corec-v2-followup/graphics/oom_handler.py

173 lines
14 KiB
Python

"""EXPERIMENTAL prospective O1/O2 handler. No allocator or admission policy."""
import errno,re,time,subprocess
from pathlib import Path
import cases,oom
from gate import sha,need
from geometry import solid_point,exact,count
from engine import COLORS
def opening_before_map(raw,popup,parent):
# WHY: native no-memory can precede configure/MAP. CONTRACT/INVARIANT:
# validate actual serial/grab/parent wire, never await the failed popup MAP.
rows=re.findall(rf'{popup} CREATE wl=(\d+) xdg=(\d+) role=(\d+) parent={parent} parent_xdg=(\d+)',raw)
need(len(rows)==1,'unique failed popup identity');wl,xdg,role,parent_xdg=map(int,rows[0])
parent_ids=re.findall(rf'{parent} CREATE wl=(\d+) xdg=(\d+) role=(\d+)',raw)
need(len(parent_ids)==1 and int(parent_ids[0][1])==parent_xdg,'failed popup exact parent')
grabs=re.findall(rf'{popup} GRAB_REQUEST origin=wl_pointer.button target={parent} serial=(\d+) used=(\d+)',raw)
need(len(grabs)==1 and grabs[0][0]==grabs[0][1] and int(grabs[0][0])>0,'failed popup real grab')
serial=int(grabs[0][0]);button=raw.index(f'POINTER_BUTTON target={parent} serial={serial} button=272 state=1')
wire=re.search(rf'wl_pointer#\d+\.button\({serial}, \d+, 272, 1\)',raw)
create=re.search(rf'xdg_surface#{xdg}\.get_popup\(new id xdg_popup#{role}, xdg_surface#{parent_xdg}, xdg_positioner#\d+\)',raw)
grab=re.search(rf'xdg_popup#{role}\.grab\(wl_seat#\d+, {serial}\)',raw)
need(wire is not None and create is not None and grab is not None and wire.start()<button<create.start()<grab.start(),'failed grab wire association')
return dict(popup=popup,parent=parent,serial=serial,wl=wl,xdg=xdg,role=role,parent_xdg=parent_xdg)
def mapped_root(raw):
proof=opening_before_map(raw,'POPUP_A','TOPLEVEL')
need(raw.count('POPUP_A MAP ')==1 and 'POPUP_A DESTROY ' not in raw and 'POPUP_A POPUP_DONE ' not in raw,'O2 same rootA mapped')
cfg=re.findall(r'POPUP_A ROLE_CONFIGURE x=(-?\d+) y=(-?\d+) w=(\d+) h=(\d+)',raw)
ack=re.findall(r'POPUP_A XDG_CONFIGURE serial=(\d+) ack',raw)
need(len(cfg)==len(ack)==1,'O2 unique rootA configure/ACK')
x,y,w,h=map(int,cfg[0]);serial=int(ack[0]);xdg=proof['xdg'];role=proof['role'];wl=proof['wl']
rolewire=re.search(rf'xdg_popup#{role}\.configure\({x}, {y}, {w}, {h}\)',raw)
ackwire=re.search(rf'xdg_surface#{xdg}\.ack_configure\({serial}\)',raw)
nativecfg=re.search(rf'xdg_surface#{xdg}\.configure\({serial}\)',raw)
attach=re.search(rf'wl_surface#{wl}\.attach\(wl_buffer#(\d+), 0, 0\)',raw)
commits=list(re.finditer(rf'wl_surface#{wl}\.commit\(\)',raw))
need(all((rolewire,ackwire,nativecfg,attach)) and len(commits)==2,'O2 rootA native configure/map evidence')
buffer=attach[1]
created=re.search(rf'create_buffer\(new id wl_buffer#{buffer}, 0, {w}, {h}, {w*4}, 1\)',raw)
need(created is not None and commits[0].start()<rolewire.start()<nativecfg.start()<ackwire.start()<created.start()<attach.start()<commits[1].start()<raw.index('POPUP_A MAP '),'O2 rootA native ACK/SHM order')
return dict(proof,configure=[x,y,w,h],ack=serial,buffer=int(buffer))
def expected_disconnect(raw,code):
# WHY/CONTRACT/INVARIANT: retain raw no_memory code1 and client errno;
# arbitrary errors, server death and missing EXIT never become normal close.
errors=re.findall(r'wl_display#\d+\.error\([^\n]*?, (\d+), [^\n]*\)',raw)
exits=re.findall(r'EXIT error=(\d+)',raw)
need(code in (0,1) and errors==['1'] and len(exits)==1 and int(exits[0]) in (errno.ENOMEM,errno.EPROTO),'expected no-memory disconnect not established')
return dict(exit=code,wire_error=errors,display_errno=exits,expected_enomem=True)
def retire_enomem(s):
p=s.proc['owner'];raw=s.log('owner');proof=expected_disconnect(raw,p.poll())
need(s.find('owner') is None,'disconnected owner remains in IPC')
proof.update(name='owner',pid=p.pid,status='PASS',meaning='EXPECTED_ENOMEM_DISCONNECT; not ordinary close',raw_log=raw)
s.closed.append(proof);s.record('owner-enomem-disconnect',**proof);return proof
def availability(s):
need(s.comp.poll() is None,'critical compositor death')
exe=Path(f'/proc/{s.comp.pid}/exe')
need(exe.resolve()==Path(s.auth['binary']).resolve() and sha(exe)==s.auth['binary_sha256'],'critical compositor PID/executable changed')
maps=Path(f'/proc/{s.comp.pid}/maps').read_text()
paths={str(Path(line.split()[-1]).resolve()) for line in maps.splitlines() if len(line.split())>=6 and line.split()[-1].startswith('/') and 'libwlroots' in line.split()[-1]}
need(paths=={s.auth['wlroots']} and sha(s.auth['wlroots'])==s.auth['wlroots_sha256'],'critical mapped wlroots changed')
build=s.command_process(['readelf','-n',str(exe)])
ids=re.findall(r'Build ID: ([0-9a-f]+)',build.stdout)
need(build.returncode==0 and len(ids)==1 and ids[0]==s.auth['build_id'],'actual availability BuildID mismatch')
need(not any('libscenefx' in line for line in maps.splitlines()),'unexpected dynamic SceneFX in OOM binary')
return dict(pid=s.comp.pid,exe=str(exe.resolve()),sha256=sha(exe),build_id=ids[0],wlroots=list(paths),maps=maps,scenefx_link='static',swaysock=str(s.sock),captured_before_cleanup=True)
def availability_certificate(identity,expected,snapshot,neighbor_raw,buttons):
# WHY: a final exit0 is not a post-injection live availability observation.
# CONTRACT: exact surviving PID/exe/BuildID/maps + private IPC + fresh wire
# press/release, saved before cleanup. INVARIANT: no superficial PASS receipt.
need(identity.get('captured_before_cleanup') is True and identity['pid']==expected['pid'],'availability PID/time scope')
need(identity['sha256']==expected['binary_sha256'] and identity['build_id']==expected['build_id'] and identity['exe']==expected['binary'],'availability ELF identity')
need(identity['wlroots']==[expected['wlroots']] and expected['wlroots'] in identity['maps'] and 'libscenefx' not in identity['maps'],'availability mappings')
sock=Path(identity['swaysock']);need(sock.parent.name.startswith('smx-') and sock.parent.parent in (Path('/tmp'),Path('/var/tmp')),'daily/foreign socket')
need(snapshot['tree'].get('type')=='root' and snapshot['tree'].get('nodes') and len([o for o in snapshot['outputs'] if o.get('active')])==2,'live IPC tree/outputs absent')
lines=buttons.get('reference',[]);events=[re.search(r'BUTTON ns=\d+ serial=(\d+) button=(\d+) state=(\d+)',line) for line in lines]
need(len(events)==2 and all(events) and [list(map(int,e.groups()[1:])) for e in events]==[[273,1],[273,0]] and int(events[0][1])<int(events[1][1]),'neighbor fresh press/release absent')
need(not any(v for name,v in buttons.items() if name!='reference'),'wrong client got neighbor input')
for e in events:need(re.search(rf'wl_pointer#\d+\.button\({e[1]}, \d+, 273, {e[3]}\)',neighbor_raw),'neighbor actual wire absent')
return dict(status='PARTIALLY_FEASIBLE',captured_before_cleanup=True,identity=identity,snapshot=snapshot,neighbor_buttons=buttons,neighbor_wire=neighbor_raw)
def native_root_success(raw,pid):
lines=[line for line in raw.splitlines() if 'SMX_OOM result ' in line and 'role=root_popup' in line and 'tree=' in line and 'failures=0' in line]
need(len(lines)==1,'O2 rootA native success identity missing/ambiguous')
fields=dict(re.findall(r'([a-z_]+)=([^ ]+)',lines[0]))
need(fields.get('pid')==str(pid) and fields.get('tree') not in (None,'(nil)','0x0'),'O2 native rootA success')
return fields
def popup_pixels(pair,baseline,failed=None,final=False):
# WHY: exact H2 alone misses stale failed/retired popup pixels on H1.
# CONTRACT/INVARIANT: inspect both outputs; only surviving A is allowed post B.
exact(baseline,pair[1])
forbidden=('POPUP_A','POPUP_B') if final else (failed,)
for role in forbidden:
need(role in ('POPUP_A','POPUP_B'),'failed role required')
need(all(count(image,COLORS[role])==0 for image in pair),'failed/retired popup pixels '+role)
return dict(status='PASS',outputs=['HEADLESS-1','HEADLESS-2'],forbidden=list(forbidden),H2='EXACT')
def surviving_root_input(s,post):
# WHY: a neighbor click can dismiss the old grab before A input is measured.
# CONTRACT/INVARIANT: fresh painted A, actual configure plus updated owner,
# exclusive wire pair and pointer-local agreement, before outside dismissal.
raw=s.log('owner');proof=mapped_root(raw);state=s.owner('owner')
x,y,w,h=proof['configure'];rect=[state['origin'][0]+x,state['origin'][1]+y,w,h]
point=solid_point(post[0],COLORS['POPUP_A'],s.outputs[0],s.frames['HEADLESS-1'],rect)
tails,buttons=s.click('owner',point,'right')
pair=s.delivery('owner',rect,point,tails,buttons,'POPUP_A')
return dict(status='PARTIALLY_FEASIBLE',point=point,rectangle=rect,owner=state,wire=proof,pair=pair,tails=tails,buttons=buttons)
def run(s):
s.closure_scenarios={'owner':'root_failure' if s.spec['id']=='OM-ROOT' else 'nested_failure'}
cases.init_reference(s);s.debit('chain');s.launch_client('owner',s.spec['client'],s.spec['placement']);s.owner('owner')
s.cmd(f'[con_id={s.find("owner")["id"]}] focus');state=s.calibrate('owner')
baseline=cases.stable_reference(s);root_success=None
if s.spec['id']=='OM-NESTED':
# Owner origin is corroborated below by actual pointer-local delivery.
origin=state['origin'];xy=[origin[0]+10,origin[1]+100]
tails,buttons=s.click('owner',xy,'left',True)
s.wait(lambda:'POPUP_A MAP ' in s.log('owner'));time.sleep(.15)
root_success=native_root_success(s.log('compositor'),s.comp.pid)
proof=mapped_root(s.log('owner'))
s.delivery('owner',[*origin,420,320],xy,tails,buttons,'TOPLEVEL',proof['serial'])
pre=s.pair_capture('oom-pre');popup_pixels(pre,baseline,'POPUP_A' if root_success is None else 'POPUP_B');before=s.snapshot('oom-before')
if root_success is not None:
need(count(pre[0],COLORS['POPUP_A'])>0 and count(pre[1],COLORS['POPUP_A'])==0,'nested precondition rootA sampled clip absent')
s.save('oom-rootA-before-injection',dict(native_success=root_success,wire=proof,painted_H1=True,H2='EXACT',clip_scope='sampled; native generation NOT_PROVEN'))
target='TOPLEVEL' if s.spec['id']=='OM-ROOT' else 'POPUP_A';popup='POPUP_A' if target=='TOPLEVEL' else 'POPUP_B'
xy=solid_point(pre[0],COLORS[target],s.outputs[0],s.frames['HEADLESS-1'])
stable=dict(startup=True,owner=True,neighbor=s.find('reference') is not None,reference_H2_twice=True,rootA=root_success is not None,rootA_native_success=root_success is not None)
oom.arm(s,s.oom_env,s.auth['prospective'],stable)
tails,buttons=s.click('owner',xy,'left',True)
s.wait(lambda:'SMX_OOM ENOMEM ' in s.log('compositor'))
wire=opening_before_map(s.log('owner'),popup,target)
native=oom.native_failure(s.log('compositor'),s.oom_env,s.comp.pid)
results=[line for line in s.log('compositor').splitlines() if 'SMX_OOM result ' in line and 'role='+s.oom_env['SMX_OOM_ROLE']+' ' in line]
need(len(results)==1 and 'tree=(nil)' in results[0] and 'failures=1' in results[0],'native NULL caller outcome absent')
ident=availability(s);s.save('oom-availability-identity',ident);after=s.snapshot('oom-after');s.save('oom-availability-ipc',dict(identity=ident,snapshot=after,captured_before_cleanup=True))
need(popup+' MAP ' not in s.log('owner'),'failed popup unexpectedly mapped')
owner_raw_exit=s.proc['owner'].poll();disconnected=None
if owner_raw_exit is None:
time.sleep(.15);owner_raw_exit=s.proc['owner'].poll()
if owner_raw_exit is not None:disconnected=retire_enomem(s)
post=s.pair_capture('oom-post');post_pixels=popup_pixels(post,baseline,popup)
old_root=dict(status='NOT_EXERCISED',reason='O1 has no old rootA' if root_success is None else ('EXPECTED_ENOMEM_DISCONNECT' if disconnected else 'ROOT_A_DISMISSED'),raw=s.log('owner'))
if root_success is not None and disconnected is None and 'POPUP_A DESTROY ' not in s.log('owner') and 'POPUP_A POPUP_DONE ' not in s.log('owner'):
need(count(post[0],COLORS['POPUP_A'])>0 and count(post[1],COLORS['POPUP_A'])==0,'surviving rootA sampled clipping')
old_root=surviving_root_input(s,post);old_root['native_success']=root_success
s.save('oom-rootA-post-input',old_root)
neighbor=s.find('reference');need(neighbor is not None,'surviving neighbor absent')
xy=solid_point(post[1],(48,48,56),s.outputs[1],s.frames['HEADLESS-2'])
tails,buttons=s.click('reference',xy)
# WHY/CONTRACT/INVARIANT: a surviving real popup grab may consume the
# outside click to dismiss; only a measured dismissal permits a fresh
# neighbor click. This does not claim universal click-through.
if not buttons.get('reference'):
need('POPUP_DONE ' in tails.get('owner',''),'neighbor input missing without measured outside dismissal')
s.save('oom-neighbor-outside-dismissal',dict(tails=tails,buttons=buttons))
tails,buttons=s.click('reference',xy)
lines=buttons.get('reference',[])
events=[re.search(r'BUTTON ns=\d+ serial=(\d+) button=(\d+) state=(\d+)',line) for line in lines]
need(len(events)==2 and all(events) and [list(map(int,e.groups()[1:])) for e in events]==[[273,1],[273,0]],'fresh neighbor button pair')
for e in events:need(re.search(rf'wl_pointer#\d+\.button\({e[1]}, \d+, 273, {e[3]}\)',tails['reference']) is not None,'neighbor real wire')
need(not any(v for k,v in buttons.items() if k!='reference'),'neighbor button not exclusive')
live=availability_certificate(ident,dict(s.auth,pid=s.comp.pid),after,tails['reference'],buttons)
s.save('oom-availability-live',live)
if disconnected is None:s.close_client('owner',scenario=s.closure_scenarios['owner'])
final=s.pair_capture('oom-final');final_pixels=popup_pixels(final,baseline,final=True)
s.save('oom-measurements',dict(post_pixels=post_pixels,final_pixels=final_pixels,native=native,wire=wire,rootA_success=root_success,old_rootA=old_root,before=before,after=after,availability=ident,owner_raw_exit=owner_raw_exit,owner_disconnect=disconnected,neighbor_buttons=buttons,H2_pre_post_final='EXACT',captures=8,closure_scenario=s.closure_scenarios['owner'],complete_damage='NOT_PROVEN',native_stale_listeners='NOT_PROVEN',native_no_partial_popup='NOT_PROVEN; IPC has no popup-node schema',atomic_generation='NOT_PROVEN'))
return 'PARTIALLY_FEASIBLE'