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

125 lines
9.5 KiB
Python

"""EXPERIMENTAL prospective O1/O2 handler. No allocator or admission policy."""
import errno,re,time
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')
return dict(pid=s.comp.pid,exe=str(exe.resolve()),sha256=sha(exe),wlroots=list(paths),maps=maps)
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 run(s):
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']=='O2':
# 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');exact(baseline,pre[1]);before=s.snapshot('oom-before')
target='TOPLEVEL' if s.spec['id']=='O1' 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);after=s.snapshot('oom-after')
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');exact(baseline,post[1])
old_root=dict(status='NOT_EXERCISED',reason='O1 has no old rootA' if root_success is None else 'owner disconnected or rootA dismissed')
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=dict(status='PARTIALLY_FEASIBLE',native_success=root_success)
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')
if disconnected is None:s.close_client('owner')
final=s.pair_capture('oom-final');exact(baseline,final[1])
s.save('oom-measurements',dict(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,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'