Show detached visualizers in popovers and inline ones in place. Move
through the tree with the keyboard, and scroll the hex view and the
tree separately.
Store them with the REPL cell and the address insight, as a JSON
column each, and bind the decode view to them. Older rows and wire
cells without the field decode as none.
The per-frame RGBA-to-BGRA conversion ran on the main actor
and starved the async continuations that drive injection, so
attaching to a booted emulator stalled. Move it into
nonisolated helpers that adopt() awaits, and mark
SharedFrameBuffer Sendable so it can cross the actor hop.
Race adb wait-for-device against the launcher exiting, so a
failed launch fails fast rather than blocking forever, and
reap the qemu by its gdb port when a boot dies before it
records the pid.
templates and availability spawned `emulator -list-avds` on every
render, so opening the boot sheet -- and clicking a template, which
re-queries them all -- stuttered for seconds. Cache the listing and
warm it off the main thread when the picker or sheet appears.
Collapse the two per-architecture Android entries into one; the
chosen AVD now decides the architecture, retiring the message that
wrongly claimed Frida could only instrument the other one.
Closing the display unmapped the shared frame at once, but cancelling a
task only asks it to stop: a screenshot already in flight goes on to read
through the base address, and the process dies on a page that is no
longer there.
Let the mapping go when the last reader does.
Inflating and scanning the kernel image to see whether it names vsock
takes some 28 seconds, and the launch waits for it. On Windows the
answer changes nothing: the transport is the virtio-serial hostlink
either way. A run of the emulator check goes from 66 to 39 seconds.
Which part of a run is slow, or where one stopped, was guesswork. The
notes now carry the elapsed time and say when the device, the session
and the script arrive.
An agent that oopses inside the guest looks like one that hangs while
the emulator's output is thrown away. FRIDA_EMULATOR_KERNEL_LOG names
a file to collect it in.
frida-core now reads a kernel once and answers what it names, so the
image config carries that rather than a path, and the vsock probe asks
it for the symbol instead of searching the image for the word.
That probe was never a symbol lookup: kallsyms stores its names
compressed, so the match came from whatever else in the kernel happens
to spell vsock. It took an LZ4 inflater of its own to look, which goes
with it.
An x86_64 AVD boots and streams, and its kernel now gives up its
symbols, but the agent never reaches it: the backend parks on a
breakpoint at schedule() that is never reported hit. The gate was
widened while that was being chased and should not have been.
QEMU's named-pipe chardev on Windows delivers what it writes and never
reads what is sent back, so the QMP handshake cannot complete over one.
A loopback port works, and with `server,nowait` QEMU no longer holds
the machine still until something connects -- so the pipe is opened,
held and handed down no longer.
The emulator bridges a guest's vsock to a host UNIX socket, which
Windows has none of, so the guest is reached over the virtio-serial
hostlink there whatever its kernel offers. That channel is opened
through QMP, which QEMU serves over a named pipe on Windows -- and it
holds the machine still until something connects to that pipe, then
gives the pipe up for good once the connection goes.
So the pipe is opened here, as soon as QEMU is spawned and before the
guest is waited on, and held for the barebone backend to adopt by
handle. Waiting for adb first would have deadlocked: the guest cannot
boot until the pipe is answered.
The transport carries the QMP address as a string now rather than a
socket URL, since each host spells it differently.
Also drops the single-core launch, which nothing turned out to need.
frida-core now skips the emulator instrumentation off Darwin and
carries an i386-64bit feature document, so the aarch64 gdbstub shim
is no longer what keeps an x86_64 AVD out. Its kallsyms parser still
wants a raw or gzipped arm64 Image, which an LZ4-packed x86 bzImage
is not, so the gate stays as it is.
An AVD's config names the ABI it boots, and the agent that gets
injected has to match it, so the ABI is read rather than assumed and
the flavor follows from it. Everything on a Windows host is x86_64,
where the old arm64 assumption would have asked for an agent that
does not fit.
Which is as far as it goes for now: Frida reaches the emulator's QEMU
through a gdbstub shim that resolves aarch64_cpu_gdb_write_register,
so an x86_64 guest boots, streams its display and reports its stub,
then fails inside instrumentation. The backend offers only what can
actually be injected into, and says which ABIs it did find when that
leaves nothing:
Frida can only instrument arm64 AVDs, and the ones installed are
x86-64. Create one in Android Studio.
The emulator's control channel was the only thing pulling grpc-swift
in, and with it swift-nio-ssl, which does not build on Windows at all
-- it has no branch for the platform and its PosixPort reaches for
mlock, lstat and readlink. Nothing here wants TLS: the endpoint is
plaintext on the loopback, and only seven calls are ever made.
So the wire is spoken directly. GRPCConnection opens one HTTP/2
stream per call over swift-nio-http2 and frames length-prefixed
protobuf into it; EmulatorControlClient names the seven. That drops
grpc-swift-2, grpc-swift-nio-transport, grpc-swift-protobuf and
swift-nio-ssl from the graph, along with 7840 lines of generated
service stubs. The generated messages stay.
The rest is what running it on Windows turned up:
- AVD discovery split on "\n" and trimmed only .whitespaces, so
every name kept a trailing \r and every AVD was dropped.
- An AVD config spells its paths with backslashes, which URL keeps
as ordinary characters rather than separators.
- emulator and adb want .exe, and the SDK sits under LOCALAPPDATA.
- Port reservation and process lookup move into HostPort and
HostProcessLookup, which QemuMachine's two copies also wanted.
- The launcher re-execs into QEMU there, so the discovery ini is
named after that process; the one naming this launch's debug port
is taken when the launcher's own is absent.
Frames still arrive through a mapping rather than the socket:
CreateFileMapping backs the same file:// handle the emulator is
handed elsewhere.
A guest without vsock is refused outright on Windows, where the
virtio-serial fallback would ask QEMU for a QMP UNIX socket and be
told "unix sockets are not available on windows".
LUMA_EMULATOR_DISPLAY_ONLY drives launch, control and streaming
without a guest agent. Against a Pixel_2 AVD it reports a 1080x1920
first frame and nine more in three seconds.
Android builds its x86 kernels with CONFIG_KERNEL_LZ4, so the payload
is neither gzip nor a PE and the image the scan sees is the bare
bzImage, which names none of the kernel's symbols. Teach the scan the
LZ4 legacy frame and add names(_:in:) for asking whether a kernel has
a symbol at all.
The emulator's own android-30 x86_64 kernel-ranchu is such an image:
39MB of payload behind an LZ4 stream at offset 16106, with "vsock"
inside it and nothing to find outside.
The emulator launcher hands QEMU off to launchd, so terminating
the launcher leaves the VM and its injected helper running.
Kill the QEMU process directly.
Drive the whole Android-emulator path headless -- launch an AVD, inject
the barebone agent, run one script -- for a fast feedback loop without
the UI. Links a local frida devkit named by LUMA_FRIDA_DEVKIT.
Drive the emulator's gRPC control API: stream its screen into an
embedded display through a shared-memory frame transport sized from the
device's reported resolution, re-opened when a notification says it
changed; forward pointer and key input; and take ready snapshots with
the SnapshotService.