-- trace_accept.lua -- H1/H2/H3 instrumentation for the X local-service connect. -- -- Start with -debuglog: breakpoint printf output goes to ./debug.log (not -- stdout). All addresses are bit addresses. -- -- Probe points: -- 3F9C2570 XOpenDisplay (Xlib client entry) -- 3FD3B3E0 XParseDisplay: call of service attach by name -- 3F70B060 service attach by name -- 3F70DB80 service table lookup -- 3F709840 service register (server side) -- 3F34EC50 "xserver" registration callback = local accept/next-connection -- 3F58B830 TCP connection accept (the real function, NOT 3F58B800) -- 3F3F4680 authorisation check -- 3F706A40 create_process -- 3FD41B90 XCreateSimpleWindow -- 3FD645B0 XMapWindow -- 3FD9F6B0 XSync -- -- Also snapshots frames on a trigger file: " ". local machine = manager.machine local cpu = machine.devices[':maincpu'] local dbg = machine.debugger local space = cpu.spaces['program'] if not dbg then print("trace: no debugger"); machine:exit(); return end dbg.visible_cpu = cpu local dir = os.getenv("MSG_DIR") or "." local trigger = os.getenv("MSG_TRIGGER") or (dir .. "/trigger") local screen = nil for _, s in pairs(machine.screens) do screen = s; break end local f = 0 local job = nil local armed = false local scanned = false local function u32(a) local ok, v = pcall(function() return space:read_u32(a) end) if ok then return v end return 0xdeadbeef end local function u8(a) local ok, v = pcall(function() return space:read_u8(a) end) if ok then return v end return 0 end local function name_at(p) local t = {} for k = 0, 15 do local c = u8(p + k * 8) if c == 0 then break end if c >= 0x20 and c < 0x7f then t[#t + 1] = string.char(c) else t[#t + 1] = "." end end return table.concat(t) end local function cmd(s) local ok, e = pcall(function() dbg:command(s) end) if not ok then print("trace: command failed: " .. tostring(e)) end end local function consume_trigger() local fh = io.open(trigger, "r") if not fh then return end local text = fh:read("*a") or "" fh:close(); os.remove(trigger) local prefix, count, stride = text:match("(%S+)%s+(%d+)%s+(%d+)") if not prefix then return end job = { prefix = prefix, left = tonumber(count), stride = tonumber(stride), next = f } print(string.format("trace: frame %d snapshot job %s x%d every %d", f, prefix, job.left, job.stride)) end local function service_table() local out = {} for i = 0, 31 do local p = u32(0x1e8d70 + i * 32) if p ~= 0 and p ~= 0xdeadbeef then out[#out + 1] = string.format("[%d]%08X:%s", i, p, name_at(p)) end end return table.concat(out, " ") end local function process_table() local out = {} local p = u32(0x1b3880) local n = 0 while p ~= 0 and p ~= 0x1b3880 and p ~= 0xdeadbeef and n < 24 do out[#out + 1] = string.format("%08X:%s", p, name_at(u32(p + 0xa0))) p = u32(p) n = n + 1 end return table.concat(out, " ") end local function arm() armed = true -- client side cmd('bpset 3F9C2570,,{ printf "TR OPEN entry pc=%08X a14=%08X arg=%08X\\n", pc, a14, d@(a14-32) ; g }') cmd('bpset 3F9C27B0,,{ printf "TR DISP-ALLOC a8=%08X\\n", a8 ; g }') cmd('bpset 3F9C3730,,{ printf "TR DISP-INIT a10=%08X\\n", a10 ; g }') cmd('bpset 3F9C6870,,{ printf "TR DISP-RET a10=%08X a8=%08X\\n", a10, a8 ; g }') cmd('bpset 3F9C6880,,{ printf "TR DISP-RET2 a8=%08X\\n", a8 ; g }') cmd('bpset 01840450,,{ printf "TR XH STORE-DISP a8=%08X\\n", a8 ; g }') cmd('bpset 01840570,,{ printf "TR XH STORE-ROOT a9=%08X\\n", a9 ; g }') cmd('bpset 01840840,,{ printf "TR XH STORE-WIN a8=%08X\\n", a8 ; g }') cmd('bpset 3FD3B3E0,,{ printf "TR PARSE-ATTACH pc=%08X name=%08X\\n", pc, d@(a14-32) ; g }') cmd('bpset 3F70B060,,{ printf "TR ATTACH entry pc=%08X nameptr=%08X proc=%08X\\n", pc, d@(a14-32), d@0x1b3900 ; g }') cmd('bpset 3F70BB40,,{ printf "TR ATTACH ret a8=%08X\\n", a8 ; g }') cmd('bpset 3F70DB80,,{ printf "TR LOOKUP entry nameptr=%08X\\n", d@(a14-32) ; g }') -- server side cmd('bpset 3F709840,,{ printf "TR REGISTER entry nameptr=%08X proc=%08X\\n", d@(a14-32), d@0x1b3900 ; g }') cmd('bpset 3F70AC20,,{ printf "TR REGISTER ret a8=%08X\\n", a8 ; g }') cmd('bpset 3F34EC50,,{ printf "TR ACCEPT-CB entry proc=%08X\\n", d@0x1b3900 ; g }') cmd('bpset 3F58B830,,{ printf "TR TCP-ACCEPT entry\\n" ; g }') cmd('bpset 3F3F4680,,{ printf "TR AUTH entry\\n" ; g }') -- local service: Xlib's setup write and the server's 12-byte setup read cmd('bpset 3FD3D980,,{ printf "TR SETUP-WRITE pre len=%08X\\n", d@(a13+4288) ; dump ' .. dir .. '/setup_write.bin,a13+160,128 ; g }') cmd('bpset 3F34F190,,{ printf "TR LOCAL-ACCEPT-WORKER entry conn=%08X\\n", a13 ; g }') cmd('bpset 3F350170,,{ printf "TR READ12 call frame=%08X conn=%08X len=%08X\\n", a13, d@(a13), d@(a13+416) ; g }') cmd('bpset 3F3501A0,,{ printf "TR READ12 ret a8=%08X\\n", a8 ; dump ' .. dir .. '/read12.bin,a13+96,64 ; g }') cmd('bpset 3F350330,,{ printf "TR READ12 complete\\n" ; g }') cmd('bpset 3F350BE0,,{ printf "TR SERVER-REPLY-WRITE call\\n" ; g }') cmd('bpset 3F350C10,,{ printf "TR SERVER-REPLY-WRITE ret a8=%08X\\n", a8 ; g }') cmd('bpset 3F9B2470,,{ printf "TR XREAD entry conn=%08X buf=%08X len=%08X\\n", a9, a10, a11 ; g }') cmd('bpset 3F9B3020,,{ printf "TR XREAD ret a8=%08X expect=%08X\\n", a8, a11 ; g }') cmd('bpset 3F70D150,d@0x1b3900==414F90,{ printf "TR SRV-WRITE entry pc=%08X\\n", pc ; g }') cmd('bpset 3F70D990,d@0x1b3900==414F90,{ printf "TR SRV-WRITE ret a8=%08X\\n", a8 ; g }') -- process creation cmd('bpset 3F706A40,,{ printf "TR CREATE_PROC entry entryarg=%08X nameptr=%08X proc=%08X\\n", d@(a14-32), d@(a14-64), d@0x1b3900 ; g }') -- window calls cmd('bpset 3FD41B90,,{ printf "TR CreateSimpleWindow\\n" ; g }') cmd('bpset 3FD645B0,,{ printf "TR MapWindow\\n" ; g }') cmd('bpset 3FD9F6B0,,{ printf "TR XSync\\n" ; g }') -- xhello progress is tracked through its relocated markers (0189xxxx); -- the code offsets moved when the setf fix was inserted, so no bpsets here. print("trace: armed") end emu.register_frame_done(function() f = f + 1 if f == 1 and not armed then arm() end consume_trigger() if job and screen and f >= job.next then local out = string.format("%s/%s_frame%05d.png", dir, job.prefix, f) pcall(function() return screen:snapshot(out) end) print(string.format("trace: frame %d snapshot %s", f, out)) job.left = job.left - 1 job.next = f + job.stride if job.left <= 0 then job = nil end end if f % 100 == 0 then print(string.format("trace: f=%d PC=%08X st=%d disp=%08X root=%08X win=%08X pcb=%08X tmdisp=%08X strlen=%d", f, cpu.state['PC'].value, u32(0x01890140), u32(0x01890100), u32(0x01890110), u32(0x01890120), u32(0x01860120), u32(0xb78e0), u32(0x01890160))) local d = u32(0x01890100) if d ~= 0x55555555 and d ~= 0 and d ~= 0xdeadbeef then local s = u32(d + 0x480) local r = u32(s + 0x40) local td = u32(0xb78e0) local ts = u32(td + 0x480) local tr = u32(ts + 0x40) print(string.format("trace: f=%d disp=%08X screens=%08X root=%08X | tm disp=%08X screens=%08X root=%08X", f, d, s, r, td, ts, tr)) if u32(0x01890140) == 3 and not scanned then scanned = true local hits = {} for a = 0x00400000, 0x01000000, 0x20 do if u32(a) == ts then hits[#hits + 1] = string.format("%08X", a) if #hits >= 8 then break end end end print("trace: heap words equal to tm screens: " .. table.concat(hits, ",")) end end end if f % 500 == 0 then print(string.format("trace: f=%d services %s", f, service_table())) print(string.format("trace: f=%d procs %s", f, process_table())) end end, "vt1200-gui-trace") if machine.debugger then machine.debugger.execution_state = 'run' end