What is it?
Machine is a deterministic execution environment, a place to run your programs where nothing is left to chance. It controls time, interrupts, random numbers and input from devices, and it records all of them while your program runs. Replay the recording and the program does exactly what it did the first time, instruction for instruction.
That gives you three things to work with:
- Record. Every run keeps a log of everything that could have turned out differently.
- Replay. Play the log back and the run repeats itself. If it failed once, it fails every time.
- Branch. Stop at any point, branch from it, and try something else. The original run stays as it was.
If you have ever chased a test that fails one run in fifty, you know why this is handy. Record the failing run once, then replay it as often as you need.
You work with Machine through a client called machine, or from Python using
the SDK. To begin, install the client. It is
one command.
Concepts
There are only a few of them, and the rest builds on them.
Disk. What a machine boots. It is your program (or a whole application)
packaged with a small Linux system. You build one with machine disks build.
Session. The vCPUs and RAM you allocate, which one or more runs execute in. It is what you are billed for, from the moment you create it until you delete it, so delete it when you are done.
Run. A deterministic machine booting a disk inside a session. Every run has a name. Nearly every action is something you do to a run.
Entry. A run writes its history as a log. Each item in the log is an entry, so for example, entry #42 is always the same point in that run.
Checkpoint. A saved copy of the whole machine at one point in the log. Every run has checkpoint 0, taken at boot, and you can add your own and name them.
Branch. A run that starts from another run at some point. It shares history
with its parent up to that point and has its own after that. You create one
with machine fork or machine go. Runs that grow out of one another form a
tree.
Mark. A second name for a branch, given after the fact. The generated name
says where a branch started. A mark says what it turned out to be,
like crashed.
Points
A lot of commands take a point, which is a place in a run’s history. You can name one in several ways.
| Point | Means |
|---|---|
^booted |
the checkpoint named booted |
#42 |
entry 42 |
1.5s |
a time on the machine’s clock |
-10ms |
ten milliseconds of machine time back from where the run is now |
now |
where the run is now |
parent |
the point this run branched from |
root |
the start of the tree |
%crashed |
the branch with the crashed mark |
A branch can only start from a checkpoint. machine go takes any of these
points, and if the one you name isn’t a checkpoint, it starts the branch at the
nearest checkpoint before it and replays the rest of the way for you. A machine
time such as 1.5s or -10ms names the first entry at the last machine time
at or before the one you asked for.
Getting started
The client
You work with Machine through a client called machine. Install it with one
command:
1 | curl https://testflows.com/machine/install -fsS | bash |
That downloads a single binary named machine over HTTPS, checks it against the
SHA-256 checksum published next to it, and puts it in ~/.local/bin, with
machine-env beside it. If the checksum doesn’t match, nothing is installed. If that folder isn’t on your PATH
yet, the installer says so and shows the line to add for your shell. Run the same
command again whenever you want to update.
Supported systems. The client runs on Linux, x86_64 and arm64, and on Macs
with Apple Silicon. It needs only the OpenSSH client, which macOS and most
Linux systems already have, and the installer never asks for root. If ssh is
missing, install it with your package manager, for example
sudo apt install openssh-client on Debian and Ubuntu. It doesn’t run on Intel Macs or natively on Windows; on Windows
you can use it inside WSL. On an ARM machine, a Mac with Apple Silicon
or arm64 Linux, the disks you build must still hold x86_64 programs; see
building disks on an ARM machine. If you
need another platform,
contact us and tell us which one. The
download page has the full list, and a way to check the
download yourself.
On a Mac, use machine-env. macOS ships its own /usr/bin/machine, which
prints the processor type, so typing machine may not run the client. Putting
the client first on your PATH would hide the system command for every other
program. machine-env, which the installer writes on every system, starts a
shell in which machine is the client and changes nothing else. Its prompt
starts with (machine), your usual shell setup is loaded, and exit leaves. It
supports zsh and bash.
1 | machine-env |
The second form runs one command in that environment and starts no shell, which
suits scripts and CI. To see which machine you get, run which machine:
~/.local/bin/machine is the client and /usr/bin/machine is the system’s. If
machine --version prints a processor type instead of a version, you are
running the system command. On Linux, if another command on your system is
also named machine, use machine-env the same way.
To read the installer first, install a specific version or into another folder, check the download by hand, or uninstall, see the download page.
Run machine with no arguments, or with --help, to see every command. Each
command has its own --help too, and that is the place to look when a flag here
is not enough.
1 | machine --help |
machine --version prints the client’s version and its license. Include the
version when you report a problem.
1 | machine --version |
1 | ---- o o o ---- |
If something does not work, machine ping checks that you can reach the
service and your sessions, and how long the round trip takes.
Signing up
Create your account on the sign-up page, or from
the terminal with machine account signup. Then sign in from the terminal.
1 | machine login |
Machine emails you a one-time code and asks for it. You stay signed in until
you sign out with machine logout. machine account show tells you who you
are signed in as.
Then set up account storage with machine account provision. The first session cannot be created until this is done.
1 | machine account provision |
For scripts and CI there is no need to sign in at all. Create an API key and
put it in the TESTFLOWS_MACHINE_API_KEY environment variable.
1 | machine account api-keys create ci --expiry 90 |
Your first run
If you have not yet signed up, signed in and set up account storage, do that first: see Signing up.
Here is a whole trip, from nothing to a branch. The disk can come from the
hello-world example if you have Docker, or from a small program you compile
if you do not.
First, create a session and make it the one your commands use.
1 | machine sessions create first --cpus 2 |
1 | ✔ Session first is running. |
1 | machine sessions use first |
1 | ✔ Now using session first in this terminal. |
Next, build a disk named hello. Take one of the two paths.
If you have Docker, pull the hello-world example and build the disk from
that image. The --platform flag asks Docker for the x86_64 image a machine
runs.
1 | docker pull --platform linux/amd64 testflows/machine-examples:latest |
If you do not, compile hello_world.c as a static executable. machine disks build --binary wraps that one file, so Docker is not needed. For a program that is not static, such as Python or Node.js, see When the program is not static.
1 |
|
1 | gcc -static -o hello hello_world.c |
On an ARM machine that binary still has to be x86_64 Linux. See building disks on an ARM machine.
Either way, create a run from the disk. The machine waits for your commands. With
--no-daemon it would boot and run to the end by itself.
1 | machine create hello-run --disk hello |
1 | ✔ Started hello-run (whUxdR33Zad9Qxc0ASfig) |
Now the run is waiting. It only moves when you drive it, and machine run is how
you drive it. The --until tasks part means “keep going until Linux is up.”
1 | machine run hello-run --until tasks |
1 | iterations: 16093 run ic: 271990491 checkpoints: 1 |
Read the last five lines the machine printed.
1 | machine console hello-run -n -5 |
1 | futex hash table entries: 256 (order: 2, 16384 bytes, linear) |
To see the full console log, use -n :. Ranges use the same colon as a Python slice. Lines are numbered from 1 and both ends count, so -n 1: is the first line through the last.
Save the machine’s state so you can come back to it, and start a branch from that point.
1 | machine checkpoint hello-run booted |
1 | Checkpointing: writing state 100%, 1s |
1 | machine fork hello-run --at ^booted --name try-1 |
1 | ✔ Forked hello-run/try-1 (whUxfLYSFJMDHzKaiBOvH) |
The branch starts exactly where booted was taken. Notice its full name,
hello-run/try-1. A branch is named after its parent, a slash, and the name you
gave it. Look at the tree.
1 | machine branches hello-run |
1 | ● hello-run (whUxdR33Zad9Qxc0ASfig) [0, ∞) ← current |
When you are done, delete the runs and then the session. A run keeps its log and checkpoints until you delete it, and a session costs money until you do.
1 | machine delete hello-run --recursive --stop --yes |
That’s the whole loop. You ran a program, saved a point in it and branched from there. Everything else in this page is a variation on it.
Disks
A disk is what a machine boots, and machine disks build makes one. You give
it exactly one source.
| Source | What it takes |
|---|---|
--binary path |
a static x86_64 executable, wrapped in a scratch image |
--from ref |
a public image, by name, with the files you add to it |
--image ref |
a Docker image of your own, as a save tar or a name in your local Docker |
--compose dir |
a Compose project directory and the images its services name |
Everything a machine runs runs in Docker Compose, inside the machine. A
Compose project is one container for each of its services. An image or a binary
is a project of one service, and --entrypoint and the arguments after -- are
that service’s entrypoint and command. A binary is no exception: --binary
wraps the executable in a scratch image, one layer holding that file and
nothing else, with no base image, no loader and no libraries, and the machine
runs that image as its one service. machine writes this image itself, so
--binary needs no Docker on your computer.
Docker and Compose
An image of your own needs Docker. A Compose project
also needs Docker Compose. Two sources need
neither. machine disks build --binary
wraps a static executable and nothing else, so a program you compile needs no
Docker. The executable has to be self-contained. The image has no loader and no
libraries, so a dynamically linked program cannot run. --from names a public
image, and the build pulls it for you.
1 | machine disks build --image myapp:latest app |
The name defaults to whatever the source is called. Use --entrypoint to
choose the executable the service runs. Flags go before the name, and the
service’s own arguments go after a --. This one runs data-race with the
argument 3:
1 | machine disks build --binary ./data-race race -- 3 |
When the program is not static
A program that is not a static executable needs an image that already contains
what it runs: an interpreter, a runtime, its libraries. Public images have
those, so name one with --from and put your files in it with --add. It
reads like a Dockerfile, FROM then ADD then the command, and you need no
Docker for it. The build pulls the image itself, so only your files are
uploaded.
A complex application, several services together, is a Compose project.
Python program
Save this as hello.py.
1 | print("hello, world") |
1 | machine disks build --from python:3.12 --add hello.py hello-py -- python hello.py |
Node.js program
Save this as hello.js.
1 | console.log("hello, world") |
1 | machine disks build --from node:22 --add hello.js hello-node -- node hello.js |
ClickHouse queries
The ClickHouse image has clickhouse local, which runs SQL with no server.
Save this as queries.sql.
1 | CREATE TABLE orders (id UInt32, customer String, amount UInt32) ENGINE = Memory; |
1 | machine disks build --from clickhouse/clickhouse-server:24.8 --add queries.sql \ |
The machine’s console shows the result, and the machine ends when the queries do.
1 | app-1 | ada 55 |
PostgreSQL queries
The PostgreSQL image starts a server, and it needs a password before it will.
--env sets a variable in the service’s environment. The image runs every
.sql file in /docker-entrypoint-initdb.d when the server first starts, so
add the queries there. Save them as queries.sql.
1 | CREATE TABLE orders (id int, customer text, amount numeric); |
1 | machine disks build --from postgres:17 --env POSTGRES_PASSWORD=secret \ |
1 | app-1 | /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/queries.sql |
The server keeps running after the queries, so this machine does not end by
itself. To run queries and have the machine end, use the
PostgreSQL project. The disk holds each --env
value as written, and a value is not changed on the way: a $ in it arrives
as a $.
Both ran in a machine with 1024MB of memory. A server that other services connect to is a Compose project.
Where added files go
--add puts a file or a directory in the image, as one more layer on top of
the public one. You can repeat it.
| You write | It lands at |
|---|---|
--add hello.py |
the image’s working directory, as hello.py |
--add conf |
the working directory, as the directory conf |
--add hello.py:/app/ |
/app/hello.py |
--add hello.py:/app/main.py |
/app/main.py |
--add conf:/etc/app/ |
/etc/app/conf, the directory inside /etc/app |
--add conf:/etc/app |
/etc/app, the directory’s contents |
The rule is the one cp -r follows: a destination that ends in / is a
directory the path goes into, and any other destination is what the path
becomes. A Dockerfile ADD of a directory copies its contents; the last row is
how you write that here.
A script that was executable still is. --add works with --binary too, for
a program that reads a file beside it.
The image is the one the name pointed at when you ran the build. The build
records its digest and pulls exactly that, so a tag that moves later does not
change your disk. You can name a digest yourself, as python@sha256:… or the
python:3.12@sha256:… that docker pull prints.
The disk’s size defaults to what the image needs plus a gigabyte. Use
--dry-run to see the numbers, and --size for more room.
--from takes public images of up to 5GB as they download. For a private
image, a larger one, or one you build yourself, use --image:
1 | docker build --platform linux/amd64 -t myapp:latest . |
An image that needs more than one service, such as a database server with a client, is a Compose project.
Compose projects
Machine-Examples has Compose
environments under compose/. Clone it, pull each image for linux/amd64
into the local Docker, then build the disk from the project directory.
machine disks build --compose packs
those images and the project. The machine runs the project with
--abort-on-container-exit, so it ends when a service exits.
1 | git clone https://github.com/testflows/Machine-Examples.git |
The same project can be run on this computer first, from its own directory, which is how to fix it without waiting for a boot. The build commands below are from the repository root.
Two services project
compose/two-services is a busybox server and a client that fetches a file
from it by the service name server. Services in a project reach each other
by name, on the network Compose gives the project.
depends_on only orders the start. The client retries, because nothing waits
for the server to accept connections. A service that takes time to become
ready should use a healthcheck, and depends_on with
condition: service_healthy.
1 | docker pull --platform linux/amd64 busybox:1.36 |
To run it on this computer first:
1 | cd compose/two-services |
ClickHouse project
compose/clickhouse is a ClickHouse server and a client that queries it.
Both services use one image, so the disk carries that image once. The client
waits until the server is healthy, prints its result, and exits, which ends
the project.
The server takes tens of seconds of machine time before it accepts a query,
so the project uses a healthcheck rather than a sleep. config.xml in the
project replaces the image’s config. It listens on IPv4 only, because the
machine’s kernel has no IPv6, and it caps the background pools, because the
image’s defaults hang startup in a small machine. CLICKHOUSE_SKIP_USER_SETUP
stops the entrypoint from restricting the default user to loopback, which
would refuse the client.
The config is mounted from ./config.xml, a path inside the project. A bind
mount from outside the project directory is refused. A named volume or a
tmpfs is created empty when the machine boots.
1 | docker pull --platform linux/amd64 clickhouse/clickhouse-server:24.8.14.39-alpine |
PostgreSQL project
compose/postgres is a PostgreSQL server and a client that runs
queries.sql against it. Both services use one image. The client prints the
results and exits, which ends the project, so this is how to run queries
against PostgreSQL and have the machine end.
The client waits until the server is healthy. The healthcheck asks over TCP,
because the image first starts a temporary server on its Unix socket alone to
set the database up, and a check on the socket would pass before the real
server is listening. ON_ERROR_STOP makes a failed query the client’s exit
code. queries.sql is mounted from the project directory.
1 | docker pull --platform linux/amd64 postgres:17 |
1 | client-1 | CREATE TABLE |
Carrying images
A service can start containers of its own, through the machine’s Docker. The images it starts are named by no service this project runs, and a machine reaches no registry, so an image that is not named here is not on the disk.
scale: 0 names an image and starts nothing. The build packs every image the
project names, including these, and Compose starts none of them.
compose/clickhouse-regression carries the images its suite starts this way.
Another image, such as ZooKeeper or MinIO, is another service.
1 | services: |
The socket is /var/run/docker.sock. /run/docker.sock is not the machine’s
socket, and the build refuses it.
./ is mounted at /opt/compose, the path the machine runs the project from.
Compose resolves a bind on the machine’s filesystem, not inside the container
that asked for it, so the two paths have to be the same. The images still have
to be in the local Docker, under those names, before the build.
What a project must satisfy
Every service names an image with a tag or a digest. An image with no tag
is a different image tomorrow, and a service that only has build has nothing
to pack. Build the image, then set image.
The images have to be linux/amd64 and already in the local Docker. A
logging driver, if the project sets one, is json-file, local, or none.
Building disks on an ARM machine
A machine is an x86_64 computer, so a disk holds linux/amd64 programs, whatever
machine you build it on. On a Mac with Apple Silicon or on arm64 Linux, Docker
builds and pulls arm64 images by default, and those cannot run in a machine.
machine disks build checks every image and refuses one that is not
linux/amd64 before uploading anything. Ask Docker for linux/amd64:
1 | docker pull --platform linux/amd64 myapp:latest |
The last one builds a Compose project’s images for linux/amd64; setting
platform: linux/amd64 on each service does the same. Docker builds them under
emulation, which is slower than a native build but works. For --binary,
compile for x86_64 Linux, for example GOOS=linux GOARCH=amd64 go build.
Disk size
By default Machine measures what the disk holds and adds a gigabyte. Use
--size to set the size you want. Not sure how big it will be? --dry-run
reports what the disk would hold without building it.
To manage your disks:
1 | machine disks list |
Deleting a disk is permanent. Machine refuses to delete one that a run still
boots, and --force deletes it anyway and takes those runs with it.
machine disks transfers shows the transfers in progress, and
machine disks cancel stops one.
Sessions
Runs live in a session, and you create one with machine sessions create.
Set up account storage first with machine account provision. The first session cannot be created until that is done.
Pick its size when you create it.
1 | machine sessions create ci-1 --cpus 4 --mem 8G |
--cpus sets the number of vCPUs, --mem the memory and --storage the
disk space. --class picks the hardware class, and the default comes from your
plan. The name is optional, so leave it off and Machine makes one up. Machine waits until the session is running and tells you when it
is. Add --no-wait if you would rather it didn’t.
Creating a session doesn’t switch you to it. Tell Machine which one to use.
1 | machine sessions use ci-1 |
machine sessions use applies to the terminal you ran it in. For scripts, set
TESTFLOWS_MACHINE_SESSION=ci-1 instead, and every command goes to that session.
If you only have one session, machine sessions use needs no name at all.
After that, every command that needs a machine goes to ci-1. You can
send one command somewhere else with -s, for example
machine -s ci-2 runs.
You manage the rest of your sessions like this:
1 | machine sessions list |
✋ Delete sessions you are not using. A session costs from the moment you create it, whether or not anything is running in it.
Running a machine
Creating a run
machine create makes a run from a disk and boots it. The run’s name goes
first and is optional. Without one, Machine makes up a name.
1 | machine create app --disk app --cpus 2 --mem 512 |
The defaults are 1 CPU and 256 MB of memory. A few other options are worth knowing about:
| Option | What it does |
|---|---|
--no-daemon |
boot and run to the end by itself, instead of waiting for your commands |
--pin-cpu n |
run on host CPU n, for this start only (the default picks the least-loaded) |
--like run |
start from another run’s options, then apply the ones you give |
--label name=value |
attach a label to the run, as many times as you like |
--rng, --rng-seed n |
give the machine random numbers, seeded with n (the default is 0) |
--fs-sync |
write to the disk as the program writes, instead of later |
--no-wait |
return as soon as the request is accepted |
Change the seed and the program sees different random numbers. Keep it and it
sees the same ones every time. This one is only about the random numbers the
machine asks for. For exploring thread schedules there is another seed, on
machine run, covered next.
Driving it
A run executes only while you drive it. machine run is the way, and --until
says when to stop. (A run created with --no-daemon answers
✗ Machine not in control mode, because the machine isn’t waiting for you.)
1 | machine run app --until tasks |
Each batch of work ends when the machine’s kernel talks to Machine, so a plain
machine run can stop sooner than you expect. With --until, Machine keeps
going, batch after batch, until what you asked for is true. If it never gets
there, the command exits with status 3.
These are the conditions you can wait for. machine run --until and
machine wait --for take the same ones.
| Condition | True when |
|---|---|
running, paused, stopped, halted |
the machine is in that state |
ready |
the machine accepts commands |
tasks |
Linux is up and its task table can be read, which is before it starts anything in userland |
console~REGEX |
the console output matches the pattern |
entry>=N |
the log has reached entry N |
entry=+N |
N more entries have been recorded |
/app:exit |
the program at /app has exited |
The last row is one example of a task condition. There are more of them under Steering programs.
--iters limits how many iterations a drive may spend, and the global
--timeout limits how long you are willing to wait. Whichever hits first wins.
Now and then the computer running Machine takes a firmware interrupt in the
middle of an entry, which makes that entry’s instruction and branch counts
unusable. Machine does not record such an entry. It goes back to the entry
before, checkpoints there and records the entry again, so the recording stays
one a replay can follow. You see nothing except a short pause. --retry sets
how many more times one entry is tried, 2 by default. If they are all
interrupted the run stops with Host interrupted the run at entry N. It is not recorded. and the machine rewind command that puts the run back at the
entry before.
Trying different schedules
A program that uses threads can behave differently depending on how they
interleave. Machine can draw how long each stretch of execution lasts, so every
run explores a different interleaving, and --seed makes those draws
repeatable.
1 | machine run app --until /usr/bin/app:exit --mode step,fast --step 1:200 --seed 1 |
--mode is how the machine runs: fast, step, step-into or free, or a
comma list to draw from. --step and --fast give the length of a stretch in
each mode, as a number or a range like 1:200. --strategy sets how the
draws are made, random (the default), golden or sweep, and --seed is the
seed for the random and golden ones.
Combine that with branches. Fork several from one checkpoint, give each its
own seed, and keep the ones that crash. The explore_seeds.py example in the
SDK does exactly that.
A run takes checkpoints for you too. --auto-checkpoints N takes one every N
entries, and 0 turns them off.
If a drive or a checkpoint is taking too long, machine cancel app cancels
whichever one is in progress.
Waiting
machine wait doesn’t drive anything. It watches a run, or several, and
returns when a condition holds.
1 | machine wait --for stopped app |
It exits with 0 when the condition is true, 2 on an error and 3 on a timeout,
so scripts can tell them apart. Remember that it only watches. Waiting for a
condition on a run nobody is driving never finishes unless you set --timeout.
With --timeout 0 it checks once, and the message tells you why it failed: The machine was waiting for commands.
Looking at it
| Command | Shows |
|---|---|
machine runs |
your runs, newest first (-w keeps the list live) |
machine describe app |
the full configuration of a run |
machine state app |
its state |
machine now app |
the last entry it recorded, and the time on the machine’s clock |
machine console app |
what the machine printed (-f follows, -n -20 shows the last 20 lines) |
machine entries app |
the entries in its log |
machine ops |
operations on runs (checkpoints, branches, switches) that are pending, unfinished or failed; one you interrupted with Ctrl-C is still listed |
machine debug log app |
the debug output of a run (machine debug err shows its error and crash log) |
machine dump app |
the machine’s registers, code and other state (--show picks the sections, --entry reads an earlier one) |
machine console also takes --entries, if you only want what a few
entries printed.
Interactive mode
When you are working on one run for a while, you can open a prompt for it.
1 | machine control app |
You issue commands to the run as slash commands, like /checkpoint,
/branches or /rewind. Most of the commands on this page have one. Type
/help to see them all. -c runs a single slash command and exits, as in
machine control app -c '/state', and machine control log app prints the
history of the commands you have issued.
Stopping and starting
1 | machine pause app |
pause freezes a running machine and resume lets it carry on. kill ends it
abruptly, where stop shuts a run down and keeps everything it recorded. start brings it
back at the end of its log. If the run had gone past its last checkpoint
when it stopped, Machine replays the recorded tail from that checkpoint and
tells you how many entries it replayed. Add --no-recover to make it
refuse instead.
start can also take a run back to an earlier point. --at replays from an
earlier checkpoint, and --read-only opens a checkpoint without running it
at all.
A run remembers the options you created it with: --no-daemon,
--single-step, --limit and --backstop-limit. Every start and fork
repeats them, so you type them once. start takes the same flags to change
them for that start only, so machine start app --daemon gives you a machine
to drive on a run created with --no-daemon. fork and detach take them
too, and the new run keeps what you gave. --pin-cpu is the exception: no run
remembers a CPU, so each start and fork picks the least-loaded one unless
you name it.
delete only removes stopped runs, unless you add --stop. It also refuses to
remove a run that has branches, unless you add --recursive. And since
machine delete --all removes every run, it asks you first.
To continue a run in a different session, publish it first from the session that has it.
1 | machine sync app |
To have a run or a disk waiting in a session before you need it, preload it.
Going the other way, machine offload lists the copies in this session that
can be dropped to free space, and -a drops every one that isn’t in use. The
durable copy stays, and starting the run brings it back.
1 | machine preload run app |
machine cleanup removes stale runs and other leftovers, and --force also
kills machines that have been orphaned.
Checkpoints and branches
Branching is cheap, because a branch shares its parent’s history instead of copying it. That is what makes it practical to try many things from one point.
Checkpoints
1 | machine checkpoint app booted |
The name is optional. Leave it off and the checkpoint is named after the entry it was taken at. Checkpoints are how you mark points you plan to come back to, like “just after boot” or “right before the request”.
A name uses letters, digits, ., - and _, and starts with a letter or
digit. It can’t be a number, because numbers name the checkpoints taken without
one, and it can’t read as a time, like 10ms, because a time names a point too.
Machine takes some of its own as well, named after the entry they were taken at
(like 10000), so machine checkpoints app lists yours next to those.
Starting a branch
machine fork creates a new branch at a point. The branch runs in its own
machine, alongside its parent, and the parent keeps running.
1 | machine fork app # at the current point |
Give a branch a name with --name. If you don’t, it is named after the
entry it landed on. Add --no-start to create the branch without starting it.
A branch’s full name is its parent’s name, a slash, and the name you gave it. So
machine fork app --name try-1 makes app/try-1, and that full name is what
you use in every other command. machine lineage try-1 says
Run not found, and machine lineage app/try-1 works.
Moving around
machine fork gives the new branch a machine of its own and leaves you where
you were. go, switch and rewind work in place instead: they change the
machine you are on. detach is the odd one out, and is explained below.
| Command | Does |
|---|---|
machine go app ^booted |
creates a branch at a point and switches this machine onto it, in place |
machine switch app app/try-1 |
moves the machine onto a branch that already exists, and never creates one |
machine detach app --at ^booted |
copies a point into a new root, a self-contained run in a tree of its own |
machine rewind app to ^booted |
puts the run back at a point and throws away what came after |
go is the one you will use most. It creates a branch the way fork does, but
it doesn’t start another machine. It moves the one you are on, in place.
1 | machine go app ^booted --name g1 |
1 | ✔ Forked app/g1 (whUxiqseEZ7Si8R4osT8K) at entry 16239 and switched onto it. |
After a go or a switch, the run you moved away from is listed as switched
and no longer running. machine start app brings it back at its tip, and tells
you how many entries it had to replay to get there. The machine you moved is now
the new branch, so talk to it by that name: app/g1.
A switch needs a branch that isn’t running somewhere else. Pointing it at a
branch that is gives ✗ Already running, so stop that one first or create it
with --no-start.
rewind changes the run itself, not a copy of it. Use it when you want to
replay from an earlier point. The run keeps its name. Machine refuses to rewind
past a branch that starts after that point, since that branch depends on entries
that would no longer exist.
switch takes a branch name, a mark (%crashed), parent or root.
detach is for leaving a tree behind. A branch normally shares its ancestors’
data, which is what keeps it cheap. detach copies everything into a new root
that owns all of its data and belongs to no tree but its own. That is much
slower than a fork, so only reach for it when you want to get rid of the
original.
If you only want a branch to have its own copy of the data, run
machine fork --rebase, which is quicker. Add --lean and Machine drops the
history it copied, so the runs it came from can be deleted.
Seeing the tree
1 | machine branches app # the whole tree this run belongs to |
branches lists the newest 25 by default. --running shows only the ones
still running, and --depth keeps one level of the tree.
Marks
When a branch turns out to be interesting, mark it.
1 | machine marks add app/try-1 crashed |
A mark is unique within its tree, so you can use it anywhere a run is named, written with a percent sign.
1 | machine console %crashed |
Replay
Replay is what makes a recording useful. A branch can play back what its parent recorded and Machine checks, step by step, that it gets the same result.
1 | machine replay app/try-1 |
Replay is strict. An entry that does not reproduce what was recorded means
the run has diverged. Before giving up on it, Machine replays that entry
again from the nearest checkpoint before it, twice by default. Use --retry
to change that, or --retry 0 to try each entry once. --to stops at a given
entry instead of the end.
The host can interrupt an entry too. A firmware interrupt on the computer
running Machine makes one entry’s instruction and branch counts unusable. That
says nothing about your run, so it is not a divergence. Replay prints
Interrupted: entry N, crossing 1: host SMI, goes back to the nearest
checkpoint before the entry and replays it again, counted against the same
--retry. A recording does the same on its own, see
Driving it.
If a run does diverge, machine diff shows how.
1 | machine diff app/try-1 |
With no second argument, diff compares the entry against the run it was
replayed from, and says ✔ No differences when they match. Otherwise it prints
a hash by default, and --show takes any of the
sections in its help: registers, stack, page tables, timers, devices,
memory and more. Name another run to compare two recordings. A difference
there only means the recordings differ. It says nothing about determinism.
There is also machine play, which plays a log back without checking for
divergence. It is for when you want to see what happened, not whether it
repeated. By default it plays the parent’s log, so it needs a branch. To play
another run’s log, name it: machine play app/try-1 app.
1 | machine play app/try-1 -n 16240:16243 |
Steering programs
By default the machine schedules itself. Sometimes that isn’t what you want. A bug that needs one thread to run past another will not show up if the scheduler never picks that order. Machine lets you pick it.
Everything here is recorded, so a replay repeats it exactly.
Looking at tasks
1 | machine tasks app |
That lists the tasks in the machine, one row per thread, with the process each one
belongs to. You will want a thread’s id, or a program’s path, in the commands
below. machine syscalls app and machine ints app list the kernel’s
system calls and interrupt vectors.
Focus
Machine matches a program by its absolute path in the machine. Tell it which programs to watch and steer.
1 | machine focus app add /app |
You can also focus on a cgroup (cgroup=/path) or a single process
(tgid=12). focus remove and focus clear take them off again.
Once a program is focused you can wait for things that happen to it. The task
conditions take the form program:what, where what is one of:
| What | Means |
|---|---|
exec |
the program started |
exit |
the program exited |
sched |
one of its threads was given a CPU |
desched |
one of its threads gave its CPU up |
on |
a thread is running its own code |
kernel |
a thread is running in the kernel |
off |
the thread is not on a CPU at all |
Holding threads
1 | machine tasks app hold 56 # take thread 56 off the run queue |
Each of these takes effect at the next scheduling decision. If you hold the
last thread that could run, there is no next decision coming, so force one
with machine irq app preempt --vcpu all.
There is also pin, which places a thread on the vCPUs you choose the next
time it wakes. Run machine tasks pin --help to see how to name them.
Two more things you can ask of tasks. machine tasks app exits lists the
tasks that have left and how each one ended, and machine tasks app cgroups
groups them by cgroup. --focused shows only the tasks you are steering.
Plans
When one rule isn’t enough, write a plan file. A plan is a list of clauses, and each one says when it applies and what to do.
1 | when /test:on mode step,fast fast=1:100k step=1:200 |
Machine tries the clauses in order and the last match wins, so a later
clause overrides an earlier one. else covers whatever nothing matched.
Each when names a state of a program (/test:on is the program running its
own code, and /test:kernel is the program running in the kernel). After that come the
things to choose for each stretch of execution it covers, such as the mode, how
many instructions to run, or what to do with the scheduler. machine plan
--help lists all of them.
1 | machine plan app plan.txt |
Interrupts and time
Inject an interrupt by name or number.
1 | machine irq app preempt --vcpu 1 |
The interrupt is queued, then delivered at the next boundary and recorded there, so a replay delivers it at the same place.
Machine time is not the host’s. machine vtime app rate
changes how fast it passes, and machine vtime app add pushes it forward
without running anything.
1 | machine vtime app add 5s |
There is no way back. The machine’s clock only goes forward, so to return to an
earlier time, branch there with machine go app 1.5s.
Reading the disk
You can read files from a run’s disk without touching the run. Reads are pinned to a checkpoint, so they never change how the machine executes.
1 | machine artifacts ls app /var/log |
Add @ref to the run to read the disk as it was at a checkpoint or an entry.
1 | machine artifacts cat app@booted /etc/hostname |
Account and billing
Most of what the account page on the website does, the client does too.
| Command | Does |
|---|---|
machine account show |
shows your account |
machine account provision |
sets up account storage, which the first session needs |
machine account credits |
shows your usage credits |
machine account activity |
lists what they were spent on |
machine account products list |
lists what is for sale |
machine account buy usage |
opens checkout for a pack of usage credits, sized in euros as listed by products |
machine account buy plan |
subscribes to a plan, or switches tier if you already have a paid one |
machine account upgrade, machine account downgrade |
moves you to a higher or lower plan |
machine account cancel |
cancels the subscription: the plan stays until it renews, then drops to Free, and your usage credits stay |
machine account invoices |
lists your invoices |
machine account orders |
lists your purchases, and resumes or cancels a pending checkout |
machine account payment |
updates your payment method |
machine account portal |
opens your billing settings |
machine account api-keys |
creates, lists, deletes and sets the expiry of API keys |
machine account devices |
lists where you are signed in (--revoke signs one out) |
machine account email |
changes your email address |
machine account close |
closes the account, in two steps (--cancel stops it) |
payment, portal and cancel open a browser. Add --link to print the
address instead.
How credits are granted and used is in the usage credit terms.
Runs, logs and disks take up space in your account. machine storage show says
how much, and machine storage prune frees the space that deleted runs and
disks left behind. machine storage prune --status shows the current or last
prune.
Scripting
Everything the client does can be done from a script.
-o jsonprints machine-readable output.-o rawprints text without formatting values. Put the flag before the command, likemachine -o json runs.-qturns off progress output and spinners.--timeout sgives up after that many seconds. It goes before the command too. The default is no limit, and0means check once.--no-colorsturns off colors.- Waiting commands exit with 3 on a timeout and 2 on an error.
1 | machine -q --timeout 300 run app --until tasks || echo "boot did not finish" |
Commands
machine --help lists these commands, in this order. The options apply to every
command. Add --help after a command to list its own options. Where a command
takes further commands, those follow it, so each one has a heading.
Options
| Option | Does |
|---|---|
-o, --output |
Output format: text, wide, raw, or json. The default is text. Wide adds columns to list output. |
--version |
Show version and license. |
--no-colors |
Disable terminal color highlighting. |
--debug |
Show the messages exchanged with the service. |
--api-url |
Cloud API base URL. Overrides TESTFLOWS_MACHINE_API_URL and the default. |
-s, --session |
Target session, by name, id, or unique prefix. |
--timeout |
Give up after S seconds, for commands that wait. The default is no limit. 0 means check once. |
-q, --quiet |
No progress or spinners. |
machine runs
List runs. See Looking at it.
Lists runs from the durable catalog, newest first.
The listing shows the newest 25. Use --limit for a different page size. Naming
a run limits the listing to its tree. Use --roots for one row per tree, and
-o wide or --no-trunc for full run ids.
1 | usage: machine runs [-h] [-s [^]state] [--running] [--roots] [--label name=value] [--flag name[=value]] [--disk name|id] [--parents mode] [--synced | --not-synced] [--since date|age] [--until date|age] [--sort field] [--reverse] [--limit n] |
| Argument | Does |
|---|---|
root |
Show one tree, by its root |
| Option | Does |
|---|---|
-s, --state [^]state |
Filter by state: running, paused, recovering, read-only, stopped, killed, terminated, died, failed (repeat = OR; ^ excludes) |
--running |
Show only running runs, the same as -s running |
--roots |
One row per tree instead of every run, with its running N/M |
--label name=value |
Filter by a label (repeatable; matches all) |
--flag name[=value] |
Filter by a run flag (repeatable; matches all) |
--disk name|id |
Filter by the disk a run boots |
--parents mode |
Filter by where a run reads data parents: local, remote |
--synced |
Show only runs published to shared storage |
--not-synced |
Show only runs with nothing published |
--since date|age |
Since a date or age |
--until date|age |
Until a date or age |
--sort field |
Sort field (default: created) |
--reverse |
Reverse sort order |
--limit n |
Show at most n runs (default: 25) |
--offset n |
Skip the first n runs |
--no-trunc |
Show full run ids |
--size |
Measure and show size |
-w, --watch |
Watch for changes |
machine ops
List operations. See Looking at it.
Lists operations on runs and how they finished.
The listing shows the newest 25. Use --limit for a different page size. Shows
pending, unfinished and failed by default; use -s done for the rest. A rewind
left unfinished is owed by its run, and the run’s next start finishes it. An
operation outlives the command that started it, so one interrupted with Ctrl-C
is still found here by its id. For work on the account’s repository, see machine
storage ops.
1 | usage: machine ops [-h] [--op-id id] [-s [^]state] [--type type] [--since date|age] [--until date|age] [--limit n] [--offset n] [--no-trunc] [-w] [run] |
| Argument | Does |
|---|---|
run |
Show operations on one run |
| Option | Does |
|---|---|
--op-id id |
Show one operation, by its id (ignores -s and --type) |
-s, --state [^]state |
Filter by state: pending, unfinished, done, failed (default: pending, unfinished, failed; repeat = OR; ^ excludes) |
--type type |
Filter by kind: checkpoint, branch, switch (repeat = OR) |
--since date|age |
Since a date or age |
--until date|age |
Until a date or age |
--limit n |
Show at most n operations (default: 25) |
--offset n |
Skip the first n operations |
--no-trunc |
Show full run ids |
-w, --watch |
Watch for changes |
machine describe
Describe a run. See Looking at it.
Show full configuration and details of a run.
1 | usage: machine describe [-h] run |
| Argument | Does |
|---|---|
run |
Run name |
machine state
Get run state. See Looking at it.
Show the current state of a run.
1 | usage: machine state [-h] run |
| Argument | Does |
|---|---|
run |
Run name |
machine now
Show where a run is. See Looking at it.
Shows where a run is.
Prints the entry the run last recorded, the vtime it carries and the position
within that vtime, then the vCPU that ran and its RIP. The vtime and entry
VALUES are what --at accepts; the line around them is not an entry. Nothing
moves.
1 | usage: machine now [-h] run |
| Argument | Does |
|---|---|
run |
Run name |
machine create
Create a new run. See Creating a run.
Creates a new run and brings it up.
The machine waits for commands; --no-daemon runs it to completion instead. The
run keeps its run options, --daemon, --single-step, --limit and
--backstop-limit, and every later start and fork repeats them unless one names
others. Waits until the machine accepts commands. Use --timeout to bound the
wait, or --no-wait to return once the request is accepted.
1 | usage: machine create [-h] [--like run] [--label name=value] [--no-wait] [--disk disk] [--mem size] [--cpus n] [--mem-type {auto,hugepages}] [--mem-mode {auto,map,lazy}] [--fs-sync | --no-fs-sync] [--fs-dirsync | --no-fs-dirsync] |
| Argument | Does |
|---|---|
name |
Name for the new run, defaulting to a generated one |
| Option | Does |
|---|---|
--like run |
Inherit options from another run, overridden by explicit ones |
--label name=value |
Attach a label, repeatable; an empty value removes an inherited one |
--no-wait |
Return without waiting |
Machine options
| Option | Does |
|---|---|
--disk disk |
Disk to boot |
--mem size |
Memory, MB or with a K/M/G/T suffix (default: 256) |
--cpus n |
Number of CPUs (default: 1) |
--mem-type {auto,hugepages} |
Machine RAM backing: auto (default) or hugepages (local only) |
--mem-mode {auto,map,lazy} |
Cold RAM restore: auto (default), map (4K pages only), or lazy (any backing) |
--fs-sync, --no-fs-sync |
Mount filesystem with sync (default: off) |
--fs-dirsync, --no-fs-dirsync |
Mount filesystem with dirsync (default: off) |
Device options
| Option | Does |
|---|---|
--fb type |
Framebuffer type: vnc, gtk, sdl, or none |
--rng, --no-rng |
Enable virtio RNG |
--rng-seed n |
RNG seed (default: 0) |
Execution options
| Option | Does |
|---|---|
--daemon, --no-daemon |
Wait for commands instead of running to completion (default: on) |
--single-step, --no-single-step |
Run in step mode (applies only for non-daemon runs) |
--rdtsc-exit, --no-rdtsc-exit |
Record an entry for every RDTSC exit (applies only for non-daemon runs) |
--limit n |
Instructions per dispatch, 0 to leave it to the machine (default: 0) |
--backstop-limit n |
Step modes only: machine instructions a dispatch may run without stepping before it ends, 0 for the machine default (default: 0) |
--pin-cpu n |
Pin to this host CPU (default: the least-loaded) |
--vtime-base n |
The vtime base (Unix timestamp, default: 2026-03-01) |
--vtime-rate rate |
The vtime rate (float or num/den, default: 10) |
--vtime-io-cost ns |
IO exit vtime cost in ns (default: 2000) |
--vtime-rdtsc-cost ns |
RDTSC exit vtime cost in ns (default: the IO cost) |
--vtime-pause-cost ns |
Vtime cost of each PAUSE in ns (default: 2000) |
--auto-checkpoints n |
Checkpoint every n entries, 0 for never (default: scaled by memory, 10000 per 512MB) |
Run log options
| Option | Does |
|---|---|
--side-dump, --no-side-dump |
Record a per-entry debug dump in the run log (default: off) |
--side-trace, --no-side-trace |
Record a per-entry execution trace in the run log (default: off) |
machine fork
Run a new branch concurrently. See Starting a branch.
Forks a new branch from a run and starts it alongside the parent.
The parent keeps running and the terminal stays attached to it. Without --at,
the branch is forked at the current point, reusing a checkpoint there if one
exists.
The branch keeps the parent’s run options; --daemon, --single-step,
--limit and --backstop-limit replace them, and the branch keeps those
instead. Its machine runs on the least-loaded host CPU unless --pin-cpu names
one.
Waits until the branch accepts commands. Use --timeout to bound the wait, or
--no-wait to return once the request is accepted.
1 | usage: machine fork [-h] [--at point [point ...]] [--name name] [--rebase] [--depth n] [--lean] [--no-start] [--side-dump | --no-side-dump] [--side-trace | --no-side-trace] [--no-wait] [--daemon | --no-daemon] |
| Argument | Does |
|---|---|
run |
Run to fork from |
| Option | Does |
|---|---|
--at point [point ...] |
Point the new branch starts at: ^checkpoint, #entry, a time, -10ms back, now, parent [n], root, or %mark |
--name name |
Name for the new branch, defaulting to an address: the entry it lands on, and for detach the run it copied as well |
--rebase |
Copy the ancestors’ data into the new branch instead of sharing it |
--depth n |
With --rebase, how many root-side ancestors to keep (default 1: the root) |
--lean |
With --rebase, drop the absorbed history so those runs become deletable |
--no-start |
Create the branch without starting it |
--side-dump, --no-side-dump |
Record a per-entry debug dump, whatever the run it comes from records |
--side-trace, --no-side-trace |
Record a per-entry kernel trace, whatever the run it comes from records |
--no-wait |
Return without waiting |
--daemon, --no-daemon |
Wait for commands instead of running to completion (default: the run’s) |
--single-step, --no-single-step |
Run in step mode, for non-daemon runs (default: the run’s) |
--limit n |
Instructions per dispatch, 0 to leave it to the machine (default: the run’s) |
--backstop-limit n |
Step modes only: machine instructions a dispatch may run without stepping before it ends, 0 for the machine default (default: the run’s) |
--pin-cpu n |
Pin to this host CPU (default: the least-loaded) |
machine stop
Stop a run. See Stopping and starting.
Stops a running machine gracefully.
Waits until the machine is gone, as with wait --for stopped. A machine that
has already exited is a success. Use --timeout to bound the wait, or
--no-wait to return once the request is accepted.
1 | usage: machine stop [-h] [-a] [--dry-run] [--no-wait] [run] |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
-a, --all |
Stop all running machines |
--dry-run |
List what would be stopped without stopping it |
--no-wait |
Return without waiting |
machine pause
Pause a run. See Stopping and starting.
Pauses a running machine.
Waits until it is paused. Use --timeout to bound the wait, or --no-wait to
return as soon as the request is accepted.
1 | usage: machine pause [-h] [--no-wait] run |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
--no-wait |
Return without waiting |
machine resume
Resume a run. See Stopping and starting.
Resumes a paused machine.
Waits until it can accept commands. Use --timeout to bound the wait, or
--no-wait to return as soon as the request is accepted.
1 | usage: machine resume [-h] [--no-wait] run |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
--no-wait |
Return without waiting |
machine kill
Kill a run. See Stopping and starting.
Kills a machine, saving its state first when it still can.
The machine is asked to stop and save, as stop does, and is signalled if it does
not go within a few seconds; a machine that did not save has its tip replayed by
the next start, and kill says so. Waits until it is gone, the same as wait
--for stopped. Already gone is success. Use --timeout to bound the wait, or
--no-wait to return as soon as the request is accepted.
1 | usage: machine kill [-h] [--no-wait] run |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
--no-wait |
Return without waiting |
machine start
Start a run. See Stopping and starting.
Starts an existing run.
A stopped run resumes at its tip. A run that stopped past its last checkpoint
replays its recorded tail from that checkpoint to reach the tip, and says how
many entries it replayed; --no-recover refuses instead. A branch that was
created but never ran starts in a new process, and its parent keeps running.
The machine runs with the run’s own run options; --daemon, --single-step,
--limit and --backstop-limit replace them for this start only, and
--pin-cpu names the host CPU, the least-loaded by default.
Waits until the machine accepts commands. Use --at to replay from an earlier
checkpoint, --read-only to open a checkpoint without replaying, --force to
stop a running instance first, --timeout to bound the wait, or --no-wait to
return once the request is accepted.
1 | usage: machine start [-h] [-f] [--read-only] [--recover | --no-recover] [--at checkpoint] [--parents mode] [--daemon | --no-daemon] [--single-step | --no-single-step] [--limit n] [--backstop-limit n] [--pin-cpu n] [--no-wait] run |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
-f, --force |
Stop a running instance first, then start |
--read-only |
Start read-only at a checkpoint, unable to run or record |
--recover, --no-recover |
Replay a tip that was never checkpointed from the last checkpoint (default: on) |
--at checkpoint |
Checkpoint to replay to the tip from, or to open with --read-only |
--parents mode |
Read data parents from the session (local) or the published tree (remote) |
--daemon, --no-daemon |
Wait for commands instead of running to completion (default: the run’s) |
--single-step, --no-single-step |
Run in step mode, for non-daemon runs (default: the run’s) |
--limit n |
Instructions per dispatch, 0 to leave it to the machine (default: the run’s) |
--backstop-limit n |
Step modes only: machine instructions a dispatch may run without stepping before it ends, 0 for the machine default (default: the run’s) |
--pin-cpu n |
Pin to this host CPU (default: the least-loaded) |
--no-wait |
Return without waiting |
machine delete
Delete a run. See Stopping and starting.
Remove a stopped run and its artifacts.
The first operand names the run to remove; -a removes every run, and --disk
removes every run that boots one disk, naming it or its id. Use -c -a to
resume interrupted delete operations.
1 | usage: machine delete [-h] [-a] [--disk name|id] [-r] [-y] [--stop] [-f] [-c] [--full-scan] [--dry-run] [run] |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
-a, --all |
Delete all runs, or cleanup all interrupted deletes (with -c) |
--disk name|id |
Delete every run that boots this disk |
-r, --recursive |
Delete branch and all descendants |
-y, --yes |
Skip the confirmation prompt |
--stop |
Stop running machines before deleting |
-f, --force |
Implies --stop, and deletes a run whose machine is already gone or whose process was replaced |
-c, --cleanup |
Resume interrupted delete operations |
--full-scan |
Scan all branches instead of using index (with -c) |
--dry-run |
List what would be deleted/recovered without acting |
machine preload
Preload into a session. See Stopping and starting.
Fetches a run or a disk onto this session.
Takes any run or disk in the catalog, so a run can be brought here to start
later, or a disk put in place before a run is created against it. -a fetches
the data parents and boot disks this session’s runs need. Already there is fine.
Needs a session; the local service has no durable copy to fetch from.
1 | usage: machine preload [-h] [-a] {run,disk} ... |
| Option | Does |
|---|---|
-a, --all |
Everything this session’s runs need |
machine preload run
Preload a run. See Stopping and starting.
Preloads a run into a session.
Fetches the run from durable storage onto the session so it is there before it is needed. Already there is fine. The disk the run boots is rematerialized with it.
1 | usage: machine preload run [-h] [-a] [run] |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
-a, --all |
Every data parent this session needs |
machine preload disk
Preload a disk. See Stopping and starting.
Preloads a disk into a session.
Fetches the account disk onto the session so it is there before a start. Already there is fine.
1 | usage: machine preload disk [-h] [-a] [disk] |
| Argument | Does |
|---|---|
disk |
Disk name or id |
| Option | Does |
|---|---|
-a, --all |
Every boot disk this session’s runs need |
machine offload
Offload session copies. See Stopping and starting.
Lists or drops this session’s local copies.
Bare offload lists this session’s own run folders and disk files that can be
dropped to free its storage, and counts the ones in use. The run and disk
subcommands drop one kind. -a drops every run and disk not in use. The durable
copy stays. Start or preload brings a run folder back; the next start
rematerializes a disk. Needs a session; the local service has nowhere to offload
to.
1 | usage: machine offload [-h] [--runs] [--disks] [-a] {list,run,disk} ... |
| Option | Does |
|---|---|
--runs |
Only session run folders |
--disks |
Only session disk files |
-a, --all |
Every run and disk not in use |
machine offload list
List what can be offloaded. See Stopping and starting.
Lists this session’s copies that can be dropped to free its storage.
Only copies on this session, and only the ones not in use; the rest are counted
below the table. Use --runs or --disks to show one kind. Omit both, or pass
both, to show both.
1 | usage: machine offload list [-h] [--runs] [--disks] |
| Option | Does |
|---|---|
--runs |
Only session run folders |
--disks |
Only session disk files |
machine offload run
Offload a run. See Stopping and starting.
Offloads a run from a session.
Drops the session’s local folder and frees its storage; the run itself stays on durable storage. Stop does not do this. Start or preload brings the folder back. Does not drop the disk the run boots.
1 | usage: machine offload run [-h] [-a] [run] |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
-a, --all |
Every run not in use |
machine offload disk
Offload a disk. See Stopping and starting.
Offloads a disk from a session.
Drops the session file; the account disk stays. A live machine that boots it refuses. The next start rematerializes it.
1 | usage: machine offload disk [-h] [-a] [disk] |
| Argument | Does |
|---|---|
disk |
Disk name or id |
| Option | Does |
|---|---|
-a, --all |
Every disk not in use |
machine branches
Show branch tree. See Seeing the tree.
Shows the branch tree.
The listing shows the newest 25 branches. Use --limit for a different page
size. Use --depth to keep one level of the tree, 0 for the roots. Naming any
run in a tree, root or branch, limits the listing to that tree.
1 | usage: machine branches [-h] [--running] [--depth n] [--since date|age] [--until date|age] [--sort field] [--reverse] [--limit n] [--offset n] [-w] [run] |
| Argument | Does |
|---|---|
run |
Show only this run’s tree |
| Option | Does |
|---|---|
--running |
Show only running branches |
--depth n |
Show only branches at this depth from the root, 0 is the root |
--since date|age |
Since a date or age |
--until date|age |
Until a date or age |
--sort field |
Sort field (default: created) |
--reverse |
Reverse sort order |
--limit n |
Show at most n branches (default: 25) |
--offset n |
Skip the first n branches |
-w, --watch |
Watch for changes |
machine lineage
Show branch lineage. See Seeing the tree.
Show the lineage chain for a branch.
1 | usage: machine lineage [-h] run |
| Argument | Does |
|---|---|
run |
Run name |
machine marks
List marks. See Marks.
Lists marks.
A mark is a second name for a branch, unique within its tree. Written %mark, it names the branch anywhere a run is named. Without a run, every tree’s marks are listed; with one, the marks of that run’s tree. No running machine is needed.
1 | usage: machine marks [-h] {list,add,remove} ... |
machine marks list
List marks. See Marks.
Lists marks.
Every tree’s, or one run’s tree when a run is named.
1 | usage: machine marks list [-h] [run] |
| Argument | Does |
|---|---|
run |
Run name |
machine marks add
Add a mark. See Marks.
Puts a mark on a branch.
Marking a branch again with the same word succeeds. A word another branch in the tree already holds is refused and stays with that branch.
1 | usage: machine marks add [-h] run mark |
| Argument | Does |
|---|---|
run |
Run name |
mark |
The mark to add |
machine marks remove
Remove a mark. See Marks.
Takes a mark off a branch.
Refused when the branch does not carry it.
1 | usage: machine marks remove [-h] run mark |
| Argument | Does |
|---|---|
run |
Run name |
mark |
The mark to remove |
machine tasks
List tasks in a machine. See Looking at tasks.
Lists the tasks running inside a machine.
One row per task, keyed by tid; tgid is the process. The table is rebuilt
whenever the machine forks, execs or exits a task. Use --vcpu to list only one
vCPU’s, --focused for only the tasks focus is steering, and cgroups to list
the same tasks grouped by cgroup.
hold takes a thread off the runqueue, so the scheduler cannot pick it. yield leaves it runnable but behind its peers, and does nothing when no peer is runnable. release is the inverse of both. All three name a tid.
slice sets how long a thread runs between scheduling decisions, as a duration with a unit: 10us, 0.6ms, 750000ns. Use default for the kernel’s own. It survives release.
Each mark acts at the vCPU’s next scheduling decision. Holding the last runnable
thread leaves nothing to run and no decision coming, so a release then needs one
forced with irq preempt --vcpu all.
exits lists the tasks that have left the table and how each one ended, newest last. The machine records them as it reaps, so the list survives a checkpoint and a replay; it keeps a bounded number and says how many it dropped.
1 | usage: machine tasks [-h] [--vcpu n] [--focused] run {cgroups,exits,hold,yield,release,slice,pin} ... |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
--vcpu n |
Only tasks on this vCPU |
--focused |
Only the tasks focus is steering |
machine tasks cgroups
List the tasks grouped by cgroup. See Holding threads.
1 | usage: machine tasks run cgroups [-h] [--vcpu n] |
| Option | Does |
|---|---|
--vcpu n |
Only tasks on this vCPU |
machine tasks exits
List the tasks that have exited, and how. See Holding threads.
1 | usage: machine tasks run exits [-h] |
machine tasks hold
Take a thread off the runqueue. See Holding threads.
1 | usage: machine tasks run hold [-h] tid |
| Argument | Does |
|---|---|
tid |
Thread id |
machine tasks yield
Put a thread behind its peers. See Holding threads.
1 | usage: machine tasks run yield [-h] tid |
| Argument | Does |
|---|---|
tid |
Thread id |
machine tasks release
Undo a hold or a yield. See Holding threads.
1 | usage: machine tasks run release [-h] tid |
| Argument | Does |
|---|---|
tid |
Thread id |
machine tasks slice
Set how long a thread runs between decisions. See Holding threads.
1 | usage: machine tasks run slice [-h] tid duration|default |
| Argument | Does |
|---|---|
tid |
Thread id |
duration|default |
Duration with a unit, or default |
machine tasks pin
Place a thread on one or more vCPUs when it next wakes. See Holding threads.
1 | usage: machine tasks run pin [-h] tid set|free|none |
| Argument | Does |
|---|---|
tid |
Thread id |
set|free|none |
vCPUs to place the thread on, such as 1 or 0-2; free for any, none for no vCPU |
machine syscalls
List the machine’s system calls. See Looking at tasks.
Lists the machine’s system calls, by number.
A name filters the list to calls whose name contains that text. A task instead
-- a tid, tgid, absolute path or cgroup=path -- shows that task’s syscall
histogram, counted while its focus entry was armed with focus add
1 | usage: machine syscalls [-h] run [name|tid|tgid|path] |
| Argument | Does |
|---|---|
run |
Run name |
name|tid|tgid|path |
Filter the list, or a task to show its histogram |
machine ints
List the machine’s interrupt vectors. See Looking at tasks.
Lists the machine’s interrupt vectors and their handlers.
One row per IDT vector the machine has a handler for, the vector in hex as the
table is indexed. A name filters to handlers whose name contains that text. A
task instead -- a tid, tgid, absolute path or cgroup=path -- shows that
task’s interrupt histogram, counted while its focus entry was armed with focus
add
1 | usage: machine ints [-h] run [name|tid|tgid|path] |
| Argument | Does |
|---|---|
run |
Run name |
name|tid|tgid|path |
Filter the list, or a task to show its histogram |
machine focus
Show or change which tasks are steered. See Focus.
Shows which tasks a machine steers.
An entry is the program’s absolute path, matched exactly, or cgroup=
1 | usage: machine focus [-h] run {add,remove,clear} ... |
| Argument | Does |
|---|---|
run |
Run name |
machine focus add
Steer another program. See Focus.
1 | usage: machine focus run add [-h] path|cgroup=path|tgid=n [+syscalls|+ints ...] |
| Argument | Does |
|---|---|
path|cgroup=path|tgid=n |
Program path, cgroup, or process id to match |
+syscalls|+ints |
Count this program’s syscalls or interrupts |
machine focus remove
Stop steering a program. See Focus.
1 | usage: machine focus run remove [-h] path|cgroup=path|tgid=n |
| Argument | Does |
|---|---|
path|cgroup=path|tgid=n |
Program path, cgroup, or process id to match |
machine focus clear
Steer nothing. See Focus.
1 | usage: machine focus run clear [-h] |
machine irq
Inject an interrupt into a machine. See Interrupts and time.
Injects an interrupt into a machine’s vCPUs.
The vector is a name from the kernel’s irq_vectors.h or a number. preempt is 0xf5, the machine’s reschedule; reschedule is 0xfd, the kernel’s own.
The vector is delivered as given. It reaches the handler the machine’s kernel has installed for it, and a vector with no handler is a spurious interrupt. Vectors below 32 are CPU exceptions and are refused.
The interrupt is queued when the command returns. It is delivered and recorded at the next dispatch boundary, and a replay repeats it there. Step first to choose that boundary.
1 | usage: machine irq [-h] [--vcpu n] [--nmi] run [vector] |
| Argument | Does |
|---|---|
run |
Run name |
vector |
Vector: a name such as preempt, or a number 32-255 |
| Option | Does |
|---|---|
--vcpu n |
Which vCPU takes it, or all (default: the last entry’s) |
--nmi |
Deliver as an NMI rather than a fixed interrupt |
machine vtime
Show or change the machine’s clock. See Interrupts and time.
Shows a machine’s clock.
The rate is nanoseconds of machine time per unit of machine work, and it decides
whether a timer is due when a dispatch ends; the instruction limit decides where
that is. Both are needed to place a tick. Use rate to change it and add to push
the clock forward without running the machine. There is no way back: the clock
drives the machine’s TSC, which never goes backwards, so use fork --at to return
to an earlier point.
1 | usage: machine vtime [-h] run {rate,add} ... |
| Argument | Does |
|---|---|
run |
Run name |
machine vtime rate
Set the rate. See Interrupts and time.
1 | usage: machine vtime run rate [-h] rate |
| Argument | Does |
|---|---|
rate |
Rate, or num/den |
machine vtime add
Push the clock forward. See Interrupts and time.
1 | usage: machine vtime run add [-h] time |
| Argument | Does |
|---|---|
time |
Time to add, such as 1s or 250ms; a bare number is nanoseconds |
machine checkpoints
List checkpoints. See Checkpoints.
Lists the checkpoints of a branch.
The listing shows the first 25 checkpoints. Use --limit for a different page
size. Read from the database, so no active run is required. The durable column
reports whether a checkpoint has been synced to durable storage. Use -o json
for an object of run_id, hosted and checkpoints rather than a bare list.
1 | usage: machine checkpoints [-h] [--durable] [--since date|age] [--until date|age] [--sort field] [--reverse] [--limit n] [--offset n] [-w] run |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
--durable |
Show only durable checkpoints |
--since date|age |
Since a date or age |
--until date|age |
Until a date or age |
--sort field |
Sort field (default: entry) |
--reverse |
Reverse sort order |
--limit n |
Show at most n checkpoints (default: 25) |
--offset n |
Skip the first n checkpoints |
-w, --watch |
Watch for changes |
machine cleanup
Remove stale runs. See Stopping and starting.
Remove stale runs and clean up leftovers.
1 | usage: machine cleanup [-h] [-f] |
| Option | Does |
|---|---|
-f, --force |
Also hard-kill orphaned machines and remove entries whose machine is already gone |
machine debug
View run debug output. See Looking at it.
Shows debug or error output from a run.
1 | usage: machine debug [-h] {log,err} ... |
machine debug log
Show debug output. See Looking at it.
Shows the debug log of a run.
Use -n to select lines, and -f to follow new output.
1 | usage: machine debug log [-h] [-n range] [-f] run |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
-n, --lines range |
Line N, A:B, A:, :B or :, or -N for the last N lines |
-f, --follow |
Follow new output (like tail -f) |
1 | line selection (-n): |
machine debug err
Show the error and crash log. See Looking at it.
Shows the error and crash log of a run.
Use -n to select lines, and -f to follow new output.
1 | usage: machine debug err [-h] [-n range] [-f] run |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
-n, --lines range |
Line N, A:B, A:, :B or :, or -N for the last N lines |
-f, --follow |
Follow new output (like tail -f) |
1 | line selection (-n): |
machine run
Run vCPU(s). See Driving it.
Runs one or more vCPU iterations.
Use --until to drive batches until a wait predicate holds. console~ means
boot-done; halted and activity=idle do not. Use --timeout before the command
to cancel the in-flight run. The default is no limit; 0 checks once without
batching.
--iters is an upper bound, not a count. A batch also ends at every context
switch of a focused task and where a task reaches its own code. Without
--until that ends the run; with it the drive continues from the next batch.
The response reports how many iterations ran.
An iteration is ONE vCPU dispatch, and every parameter is drawn for it: the
mode, the limit, the task marks and the interrupt all apply to the task on that
one vCPU. Without --vcpu a scheduling cycle dispatches every vCPU, so it
spends up to that many iterations; --iters 100 on four vCPUs buys 100
dispatches, not 400.
With --until, the two are bounds and the first to fire wins: --iters 500
--until on stops at 500 iterations or when a focused task takes a vCPU.
Without --iters nothing bounds the drive but --timeout, so --until tasks
runs a whole boot. A SPAN bounds nothing either: it draws a batch size per
dispatch, so --iters 100:500 --until on keeps going. Exit 3 whenever the
predicate is not reached.
--when bounds nothing. It names what must be true for the clause to apply to a
dispatch; a dispatch it excludes runs free, up to the crossing into the subject.
So --when /test:on --until /test:exit perturbs that program while its own
code runs, free runs while it is in the kernel or off the CPU, and stops when it
is gone. --iters then counts dispatches the clause applied to. --when takes
on, kernel and off: a state, not an edge.
This one clause is the CLI. More than one is a plan file: machine plan
1 | usage: machine run [-h] [--limit span] [--backstop-limit n] [--retry n] [--strategy name] [--seed n] [--vcpu span] [--iters span] [--until predicate] [--rip addr] [--rcx addr] [--target-ic n] [--irqblk] [--rdtsc-exit] [--no-idle-skip] |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
--limit span |
Default instructions per dispatch under fast, steps under step; fast takes 1000 or more and stops at or after it, late by an amount that depends on what the machine runs; step stops exactly; 0 means no limit under fast and one step under step; default 0 |
--backstop-limit n |
Step modes only: machine instructions a dispatch may run without stepping before it ends, 0 for the machine default (default: 0) |
--retry n |
Retries for an entry the host interrupted, default 2; 0 does not retry |
--strategy name |
How spans resolve when they name none: random, golden, sweep (default: random) |
--seed n |
Seed for the random and golden draw strategies (default: 0) |
--vcpu span |
The vCPU, or a span to draw one per dispatch (default: all) |
--iters span |
Iterations, an upper bound; 0 is unlimited (default: 1; with --until, unbounded) |
--until predicate |
Drive until this is true (same predicates as wait --for) |
--rip addr |
Target RIP address |
--rcx addr |
Target RCX value |
--target-ic n |
Target instruction count |
--irqblk |
Block IRQs during run |
--rdtsc-exit |
Record an entry for every RDTSC exit |
--no-idle-skip |
Keep machine time crawling while every vCPU is idle |
--auto-checkpoints n |
Auto-checkpoint every N entries; 0 turns it off, default the machine’s |
--mode list |
Execution mode: fast, step, step-into, free, or a comma list to draw among (default: fast) |
--fast span |
Limit fast draws when it wins (its member), overriding --limit |
--step span |
Limit step draws when it wins (its member), overriding --limit |
--step-into span |
Limit step-into draws when it wins (its member) |
--vtime-rate span |
Vtime rate for this run: n, n/d, or a span of either |
--vtime-add span |
Nanoseconds to push the clock forward before each dispatch |
--task-sched choices |
Draw a scheduler mark per dispatch for the task on that vCPU: keep, hold, yield, drain, drain-all |
--task-slice choices |
Draw that task’s slice per dispatch: keep, default, or a duration range such as 10us:600us |
--task-int choices |
Draw an interrupt for that task per dispatch: none, preempt |
--when predicate |
Apply the clause only while this holds |
machine plan
Run a multi-clause plan from a file. See Plans.
Runs a multi-clause plan from a file.
A plan file is a list of clauses. A clause is an optional when predicate and the per-dispatch axes it draws for a dispatch it matches. Clauses are tried in order and the LAST to match supplies each axis, so a later clause overrides an earlier one; else is the clause for a dispatch no when matched. Braces may group a clause, # begins a comment, and blank lines are ignored. A file of - reads the plan from standard input.
Example: when /test:on mode step,fast fast=1:100k step=1:200 when /test:kernel task-sched hold else mode fast
Example, placing thread 56 on vCPU 2 or 3 whenever it wakes, 2 three times as often as 3: else task-pin 56=golden:2[3],3[1]
A clause names one when predicate, then any of these axes. An axis is a comma list of choices, one drawn per dispatch, and [N] is an optional weight on a choice; task-slice is the exception, a colon set that takes no weights:
when
A weight makes a choice win more often: mode step[3],fast[1] draws step three
times as often as fast. keep is the choice that draws nothing: it holds that
axis’s last value, so a clause can leave an axis alone part of the time. A span
is a range written lo:hi[:strategy[:step]], strategy one of random, golden or
sweep, and one value is drawn from it per dispatch; the mode member limit,
vtime-rate, vtime-add, task-slice and the run-level --limit each accept one.
limit itself is not a clause axis; it is the default a mode with no member of
its own falls back to.
The options on this command are plan-level. --limit and --strategy are the
defaults a clause draws with when it names no span of its own, and a clause axis
overrides them for the dispatches it matches; a dispatch no clause matches runs
free. --seed seeds the draw strategies, and --until, --iters and --vcpu
bound and steer the run as on run. The clause axes and when belong in the file,
not here. The plan applies to this command’s run only; a later run without it
runs without the plan.
1 | usage: machine plan [-h] [--limit span] [--backstop-limit n] [--retry n] [--strategy name] [--seed n] [--vcpu span] [--iters span] [--until predicate] [--rip addr] [--rcx addr] [--target-ic n] [--irqblk] [--rdtsc-exit] [--no-idle-skip] |
| Argument | Does |
|---|---|
run |
Run name |
file |
Plan file of when and else clauses, and their axes, or - to read standard input |
| Option | Does |
|---|---|
--limit span |
Default instructions per dispatch under fast, steps under step; fast takes 1000 or more and stops at or after it, late by an amount that depends on what the machine runs; step stops exactly; 0 means no limit under fast and one step under step; default 0 |
--backstop-limit n |
Step modes only: machine instructions a dispatch may run without stepping before it ends, 0 for the machine default (default: 0) |
--retry n |
Retries for an entry the host interrupted, default 2; 0 does not retry |
--strategy name |
How spans resolve when they name none: random, golden, sweep (default: random) |
--seed n |
Seed for the random and golden draw strategies (default: 0) |
--vcpu span |
The vCPU, or a span to draw one per dispatch (default: all) |
--iters span |
Iterations, an upper bound; 0 is unlimited (default: 1; with --until, unbounded) |
--until predicate |
Drive until this is true (same predicates as wait --for) |
--rip addr |
Target RIP address |
--rcx addr |
Target RCX value |
--target-ic n |
Target instruction count |
--irqblk |
Block IRQs during run |
--rdtsc-exit |
Record an entry for every RDTSC exit |
--no-idle-skip |
Keep machine time crawling while every vCPU is idle |
--auto-checkpoints n |
Auto-checkpoint every N entries; 0 turns it off, default the machine’s |
machine vcpu
Get vCPU run state.
Shows the current vCPU run state.
1 | usage: machine vcpu [-h] [--vcpu n] run |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
--vcpu n |
The vCPU (default: 0) |
machine dump
Dump code, registers, page tables. See Looking at it.
Dump vCPU code, registers, or page tables.
Use -o json to emit one object per --show section, and omit --show for
every live section. Use --mem for the mem object, alone unless --show is
also set.
1 | usage: machine dump [-h] [--entry n] [--show sections] [--mem addr] [--vcpu n] run |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
--entry n |
Entry from run log |
--show sections |
One or more, comma-separated: code, regs, stack, pt, instr, trace, events, timers, debug, xsave, irq, pic, ioapic, devs, mem, disk, hash, entry, io, msrs, fullhash, all |
--mem addr |
Dump raw memory cluster: N (index), pADDR (physical), vADDR (machine address) |
--vcpu n |
The vCPU whose state and page tables are read (default: 0) |
machine checkpoint
Create a checkpoint. See Checkpoints.
Create a checkpoint of the current machine state.
1 | usage: machine checkpoint [-h] run [name] |
| Argument | Does |
|---|---|
run |
Run name |
name |
Checkpoint name (auto-generated if omitted) |
machine sync
Publish a run to shared storage. See Stopping and starting.
Publishes a run to shared storage.
Pushes the run’s folder so any session can start it. Runs in the session that holds the folder.
1 | usage: machine sync [-h] run |
| Argument | Does |
|---|---|
run |
Run name |
machine cancel
Cancel the active run or checkpoint. See Trying different schedules.
Cancel the currently active run or checkpoint operation.
1 | usage: machine cancel [-h] run |
| Argument | Does |
|---|---|
run |
Run name |
machine detach
Detach into a new root. See Moving around.
Detaches a new self-contained root from a run and starts it.
Without --at, the root is detached at the current point. The copy includes
everything the root still owns and shares nothing with the origin tree. It is
slower than fork --rebase.
The new root is named for the run it copied and the point. A second copy of the same point gets a suffix.
The new root keeps the run options of the run it copied; --daemon,
--single-step, --limit and --backstop-limit replace them, and the root
keeps those instead. Its machine runs on the least-loaded host CPU unless
--pin-cpu names one.
1 | usage: machine detach [-h] [--at point [point ...]] [--name name] [--no-start] [--side-dump | --no-side-dump] [--side-trace | --no-side-trace] [--no-wait] [--daemon | --no-daemon] [--single-step | --no-single-step] [--limit n] |
| Argument | Does |
|---|---|
run |
Run to detach from |
| Option | Does |
|---|---|
--at point [point ...] |
Point the new branch starts at: ^checkpoint, #entry, a time, -10ms back, now, parent [n], root, or %mark |
--name name |
Name for the new branch, defaulting to an address: the entry it lands on, and for detach the run it copied as well |
--no-start |
Create the branch without starting it |
--side-dump, --no-side-dump |
Record a per-entry debug dump, whatever the run it comes from records |
--side-trace, --no-side-trace |
Record a per-entry kernel trace, whatever the run it comes from records |
--no-wait |
Return without waiting |
--daemon, --no-daemon |
Wait for commands instead of running to completion (default: the run’s) |
--single-step, --no-single-step |
Run in step mode, for non-daemon runs (default: the run’s) |
--limit n |
Instructions per dispatch, 0 to leave it to the machine (default: the run’s) |
--backstop-limit n |
Step modes only: machine instructions a dispatch may run without stepping before it ends, 0 for the machine default (default: the run’s) |
--pin-cpu n |
Pin to this host CPU (default: the least-loaded) |
machine switch
Switch to a branch. See Moving around.
Switches a running machine onto a branch that exists.
The first operand names the run that switches, as with fork and detach. The second names where it goes: a branch, a mark written %mark, or parent and root. It never creates; forking a branch and switching onto it is go, and forking one without switching is fork.
1 | usage: machine switch [-h] [--no-wait] run [branch ...] |
| Argument | Does |
|---|---|
run |
The running machine that switches |
branch |
Branch to switch onto: a name, a mark written %mark, parent [n], or root |
| Option | Does |
|---|---|
--no-wait |
Return without waiting |
machine rewind
Put a run back at a point, discarding the tail. See Moving around.
Puts a run back at a point and discards what came after it.
The run keeps its id and its name. The entries, checkpoints and console output after the point are discarded.
The point is required and is any entry from #0 to the tip: an entry id after
#, a checkpoint name after ^, or a machine time. The run lands on it exactly,
and the point becomes a checkpoint. Rewinding to the tip changes nothing unless
a replay diverged there.
A point before the run was forked moves the run back along the line it was forked from. A run with a branch forked after the point is refused.
1 | usage: machine rewind <run> to <point> |
| Argument | Does |
|---|---|
run |
The machine that rewinds |
point |
Point to go back to, from #0 to the tip: ^checkpoint, #entry or a time |
machine go
Branch at a point and switch onto it. See Moving around.
Forks a branch at a point and switches onto it.
The move a walk makes most, and the only one that forks and switches in a single step. The point may be a time, -10ms back, #entry, ^checkpoint, now, parent [n], root, or a mark written %mark; each of them names a point on this run’s own line. A point that is not a checkpoint is reached by forking at the nearest one at or before it and replaying the difference.
1 | usage: machine go [options] <run> <point> |
| Argument | Does |
|---|---|
run |
The running machine that moves |
point |
Where to fork: a time, -10ms back, #entry, ^checkpoint, now, parent [n], root, or %mark |
| Option | Does |
|---|---|
--name name |
Name for the new branch, defaulting to an address: the entry it lands on, and for detach the run it copied as well |
--rebase |
Copy the ancestors’ data into the new branch instead of sharing it |
--depth n |
With --rebase, how many root-side ancestors to keep (default 1: the root) |
--lean |
With --rebase, drop the absorbed history so those runs become deletable |
--side-dump, --no-side-dump |
Record a per-entry debug dump, whatever the run it comes from records |
--side-trace, --no-side-trace |
Record a per-entry kernel trace, whatever the run it comes from records |
--no-wait |
Return without waiting |
machine replay
Replay parent entries. See Replay.
Replays recorded entries from the parent lineage.
Without a line, the parent’s entries are replayed. A fork at an entry leaves that entry with the parent and starts the branch at the next one, so the parent’s entries end at the fork. Replaying past them reports no forward entries.
Naming a line replays the entries that lead to that run, across every fork on the way. The run must be in the same tree. A line that is not this run’s own is not refused; the replay diverges at the first entry that does not reproduce.
An entry that diverges is replayed again from the newest checkpoint before it,
twice at most, and not again once it diverges the same way twice. An entry the
host interrupted is replayed again the same way and is not a divergence. Use
--retry to set how many retries; --retry 0 replays each entry once.
The replay aims its interrupt short of each recorded stop by the machine’s
default skid, so an interrupt that skids less still lands in time. Use --skid
to aim closer. A smaller skid makes a miss more likely.
1 | usage: machine replay [-h] [--to n] [--trace] [--retry n] [--skid n] run [line] |
| Argument | Does |
|---|---|
run |
Run name |
line |
Run whose line to replay, defaulting to the parent’s |
| Option | Does |
|---|---|
--to n |
Replay to entry n, defaulting to the end of the line |
--trace |
Trace each iteration one by one |
--retry n |
Retries for an entry that diverges or the host interrupted, default 2; 0 does not retry |
--skid n |
Aim the interrupt n instructions short of each recorded stop |
machine play
Play entries from a run log. See Replay.
Scripted playback from a run’s log. No divergence checking.
The source must be in this run’s tree. Divergence is not checked, so a source that is not this run’s own line is played and not refused.
1 | usage: machine play [-h] [-n range] [--include type [type ...]] [--exclude type [type ...]] run [source] |
| Argument | Does |
|---|---|
run |
Run name |
source |
Source run (default: parent) |
| Option | Does |
|---|---|
-n, --entries range |
Entry N, A:B, A:, :B or :, or -N for the last N entries |
--include type [type ...] |
Include only these event types: run, irq, exception, random, device_in, block_done, input, exit_shutdown, system_shutdown, system_reset, checkpoint, cpuid |
--exclude type [type ...] |
Exclude these event types |
machine diff
Show replay divergence details. See Replay.
Compares a run log entry against another.
With no second operand the entry is compared against the run it was replayed from, and the answer says whether the replay converged. Naming a run compares against that run, at its own entry when one is given as other:m. A difference between two recordings says that they differ, not that a replay diverged.
1 | usage: machine diff [-h] [--show sections] [--entry n] [--context n] run [other[:m]] |
| Argument | Does |
|---|---|
run |
Run name |
other[:m] |
Run to compare against, at its entry m; m defaults to --entry |
| Option | Does |
|---|---|
--show sections |
One or more, comma-separated: code, regs, stack, pt, instr, trace, events, timers, debug, xsave, irq, pic, ioapic, devs, mem, disk, hash, counts, entry, io, msrs, fullhash, all; default hash |
--entry n |
Compare specific entry N (0 = last entry) |
--context n |
Also show the N entries leading up to it, on both sides |
machine control
Interactive control mode. See Interactive mode.
Enter interactive command mode for rapid vCPU control.
Use control log with a run to read the command history instead of driving
the machine, --entries to select the commands issued at given run entries,
and -n to select commands by number.
1 | usage: machine control [-h] [--entries range] [-n range] [-c cmd] run|log [run] |
| Argument | Does |
|---|---|
run|log |
Run name, or log to read history |
run |
With log: the run whose history to read |
| Option | Does |
|---|---|
--entries range |
With log: commands at one entry, or an inclusive range |
-n, --lines range |
With log: command N, A:B, A:, :B or :, or -N for the last N commands |
-c, --command cmd |
Slash command (e.g. “/s=0@10”) |
1 | command selection (-n): |
machine wait
Wait until a run satisfies a predicate. See Waiting.
Block until a run satisfies a --for predicate.
Predicates: running, ready, tasks, stopped, paused, halted, activity=
A task predicate is [who:]what. what is exec, exit, sched, desched, on, kernel or off. who is /path, cgroup=path, tgid=N or tid=N, and omitting it means any focused task; a path or a cgroup must already be focused. Four of the seven are EDGES: exec where a task matching who entered the task table, exit where one left it, sched where a thread is given the CPU, desched where one gives it up. Three are STATES, a partition: on while the thread is on cpu in its own code, at CPL 3, which is where a forced decision can land between two of its own instructions; kernel while that thread is on cpu in the kernel; off while it is not the task on cpu at all.
A path or cgroup subject means the process. tgid=N means the process for exit and any of its threads for exec. machine focus names the task that stopped the wait.
Named runs must be in the current session. Use --all to wait for every run in
it.
Use --timeout before the command. The default is no limit; 0 checks once.
Exit codes: 0 success, 2 error, 3 timeout.
1 | usage: machine wait [-h] [--all] --for predicate [--interval s] [run ...] |
| Argument | Does |
|---|---|
run |
One or more runs (omit with --all) |
| Option | Does |
|---|---|
--all |
Wait for every run in the session |
--for predicate |
running, ready, stopped, entry=+N, console~REGEX, /path:exec, … |
--interval s |
Poll every S seconds (default: 1) |
machine console
View run console output. See Looking at it.
Shows the console of a run.
Use --entries to select the output of given entries, -n to select lines, and
-f to follow new output. An entry that printed nothing contributes nothing.
1 | usage: machine console [-h] [-n range] [--entries range] [-f] run |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
-n, --lines range |
Line N, A:B, A:, :B or :, or -N for the last N lines |
--entries range |
Output of one entry, or of an inclusive range |
-f, --follow |
Follow new output (like tail -f) |
1 | line selection (-n): |
machine entries
View run log entries. See Looking at it.
Shows run log entries.
Use -n to select entries, and -f to follow new output.
1 | usage: machine entries [-h] [-n range] [-f] run |
| Argument | Does |
|---|---|
run |
Run name |
| Option | Does |
|---|---|
-n, --entries range |
Entry N, A:B, A:, :B or :, or -N for the last N entries |
-f, --follow |
Follow new output (like tail -f) |
1 | entry selection (-n): |
machine sessions
List and manage sessions. See Sessions.
Lists and manages sessions.
Create a session, then use it so later commands target it by default.
1 | usage: machine sessions [-h] [-w] {list,create,use,show,delete,rename,connect,disconnect} ... |
| Option | Does |
|---|---|
-w, --watch |
Watch for changes |
machine sessions list
List sessions. See Sessions.
Lists sessions.
The current session is tagged.
1 | usage: machine sessions list [-h] [-w] |
| Option | Does |
|---|---|
-w, --watch |
Watch for changes |
machine sessions create
Create a session. See Sessions.
Creates a session.
Waits until it is running. Use --timeout to bound the wait, or --no-wait to
return once the request is accepted.
1 | usage: machine sessions create [-h] [--cpus n] [--mem size] [--storage size] [--class type] [--no-wait] [name] |
| Argument | Does |
|---|---|
name |
Session name (default: auto-generated) |
| Option | Does |
|---|---|
--cpus n |
Number of vCPUs (default: 1) |
--mem size |
Memory, MB or with a K/M/G/T suffix (default: class baseline) |
--storage size |
The session’s storage, GB or with a K/M/G/T suffix (default: class baseline) |
--class type |
Hardware class (default: plan default) |
--no-wait |
Return without waiting |
machine sessions use
Use a session. See Sessions.
Makes a session current so later commands target it by default.
Connects first if needed, then checks it responds before switching. Use
--timeout to bound the wait, or --no-wait to fail if a connect is needed and
the session is not running yet.
1 | usage: machine sessions use [-h] [--no-wait] [session] |
| Argument | Does |
|---|---|
session |
Session name, id, or prefix (default: the only one) |
| Option | Does |
|---|---|
--no-wait |
Fail if a connect is needed and the session is not running yet |
machine sessions show
Show a session. See Sessions.
Shows session details.
1 | usage: machine sessions show [-h] [session] |
| Argument | Does |
|---|---|
session |
Session name, id, or prefix (default: the only one) |
machine sessions delete
Delete a session. See Sessions.
Deletes a session.
The first operand names the session; -a deletes every session. Stops the
machine and discards its state. Already gone is success. Waits until a named
session is gone. Use --timeout to bound the wait, or --no-wait to return
once the request is accepted. A prune is refused while any session is live, so
-a is what storage prune needs.
1 | usage: machine sessions delete [-h] [-a] [-y] [--no-wait] [session] |
| Argument | Does |
|---|---|
session |
Session name, id, or prefix (default: the only one) |
| Option | Does |
|---|---|
-a, --all |
Delete every session |
-y, --yes |
Skip the confirmation prompt |
--no-wait |
Return without waiting |
machine sessions rename
Rename a session. See Sessions.
Renames a session.
The machine and tunnel are unchanged.
1 | usage: machine sessions rename [-h] session new-name |
| Argument | Does |
|---|---|
session |
Session name, id, or prefix |
new-name |
New session name |
machine sessions connect
Connect to a session. See Sessions.
Connects to a session.
The everyday verb is sessions use. Waits until it is running if it is still
provisioning. Use --timeout to bound the wait, or --no-wait to fail if it is
not running yet.
1 | usage: machine sessions connect [-h] [--no-wait] [session] |
| Argument | Does |
|---|---|
session |
Session name, id, or prefix (default: the only one) |
| Option | Does |
|---|---|
--no-wait |
Fail if the session is not running yet |
machine sessions disconnect
Disconnect from a session. See Sessions.
Disconnects from a session.
The session keeps running. The tunnel is shared on this machine, so every terminal loses it.
1 | usage: machine sessions disconnect [-h] [session] |
| Argument | Does |
|---|---|
session |
Session (default: the current one) |
machine disks
Manage disks. See Disks.
Lists and manages disks.
The listing shows the newest 25. Use --limit for a different page size.
1 | usage: machine disks [-h] [-q] [--no-trunc] [--deleted] [--label name=value] [--since date|age] [--limit n] [--offset n] [-w] {list,build,show,transfers,delete,rename,cancel} ... |
| Option | Does |
|---|---|
-q, --quiet |
Only show ids |
--no-trunc |
Full disk id |
--deleted |
Show removed disks |
--label name=value |
Filter by a label (repeatable; matches all) |
--since date|age |
Since a date or age |
--limit n |
Show at most n disks (default: 25) |
--offset n |
Skip the first n disks |
-w, --watch |
Watch for changes |
machine disks list
List disks. See Disks.
Lists disks.
The listing shows the newest 25. Use --limit for a different page size.
1 | usage: machine disks list [-h] [-q] [--no-trunc] [--deleted] [--label name=value] [--since date|age] [--limit n] [--offset n] [-w] |
| Option | Does |
|---|---|
-q, --quiet |
Only show ids |
--no-trunc |
Full disk id |
--deleted |
Show removed disks |
--label name=value |
Filter by a label (repeatable; matches all) |
--since date|age |
Since a date or age |
--limit n |
Show at most n disks (default: 25) |
--offset n |
Skip the first n disks |
-w, --watch |
Watch for changes |
machine disks build
Build a disk. See Disks.
Builds a disk from a binary, an image, or a Compose project.
Exactly one source. --binary wraps a static executable in an image built
around it. --from names a public image, which the build pulls; no Docker is
needed. --image takes a Docker image as a save tar or a name in the local
Docker. --compose takes a project directory and the images its services name.
A binary or an image runs as one container. Use --entrypoint to set the
executable, --env to set a variable in its environment, and pass arguments
after --. A project runs every service in it and takes none of the three. The
disk holds each --env value as written.
With --from or --binary, --add puts a file or a directory in the image, as
one more layer. Without a destination the path lands in the image’s working
directory under its own name; path:dest names another place, and a dest ending
in / is a directory.
The name defaults to what the source is called, and the size to what the pack
holds loaded plus a gigabyte, rounded up to a whole GB. Use --dry-run to see
those numbers without building anything.
1 | usage: machine disks build [-h] (--binary path | --image ref | --from ref | --compose dir) [--size size] [--dry-run] [--entrypoint path] [--add path[:dest]] [--env name=value] [--label name=value] [name] [-- args ...] |
| Argument | Does |
|---|---|
name |
Disk name |
-- args |
Arguments after -- become the service’s command |
| Option | Does |
|---|---|
--binary path |
Static x86_64 executable to wrap |
--image ref |
Docker image, a save tar or a name |
--from ref |
Public image, pulled by the build |
--compose dir |
Compose project directory |
--size size |
Disk size, MB or with a K/M/G/T suffix (default: from the pack, rounded up to a GB) |
--dry-run |
Report what the disk would hold and build nothing |
--entrypoint path |
Executable the service runs |
--add path[:dest] |
File or directory to put in the image, repeatable; dest defaults to the image’s working directory |
--env name=value |
Environment variable the service runs with, repeatable |
--label name=value |
Attach a label, repeatable; an empty value removes one the name carried |
machine disks show
Show a disk. See Disks.
Shows disk details.
1 | usage: machine disks show [-h] name |
| Argument | Does |
|---|---|
name |
Disk name |
machine disks transfers
Show transfers. See Disks.
Shows disk transfers.
Active transfers by default. Use --since to include history, or --since with
--active for in-flight transfers started in that window.
1 | usage: machine disks transfers [-h] [--active] [--since date|age] [-w] |
| Option | Does |
|---|---|
--active |
Only in-flight transfers |
--since date|age |
Since a date or age |
-w, --watch |
Watch for changes |
machine disks delete
Delete disks. See Disks.
Deletes disks.
Deletion is permanent. The name is free at once, and the bytes count against the
storage quota until the reclaim removes them. A disk a run still boots is
refused; use --force to delete it anyway and lose those runs.
1 | usage: machine disks delete [-h] [-y] [-f] name [name ...] |
| Argument | Does |
|---|---|
name |
Disk names |
| Option | Does |
|---|---|
-y, --yes |
Skip the confirmation prompt |
-f, --force |
Delete even while runs still boot it |
machine disks rename
Rename a disk. See Disks.
Renames a disk.
1 | usage: machine disks rename [-h] name new-name |
| Argument | Does |
|---|---|
name |
Current disk name |
new-name |
New disk name |
machine disks cancel
Cancel a transfer. See Disks.
Cancels an in-flight transfer.
1 | usage: machine disks cancel [-h] name |
| Argument | Does |
|---|---|
name |
Disk name |
machine storage
Manage storage. See Account and billing.
Shows and manages storage.
1 | usage: machine storage [-h] [--refresh] {show,ops,prune} ... |
| Option | Does |
|---|---|
--refresh |
Measure again instead of using the last measurement |
machine storage show
Show storage the account uses. See Account and billing.
Shows the storage the account uses.
1 | usage: machine storage show [-h] [--refresh] |
| Option | Does |
|---|---|
--refresh |
Measure again instead of using the last measurement |
machine storage ops
List storage operations.
Lists operations on the account’s storage and how they finished.
The listing shows the newest 25. Use --limit for a different page size. Shows
pending and failed by default; use -s done for the rest. These act on the
account’s repository rather than on a run, so they are listed here rather than
by machine ops, and they answer with no session.
1 | usage: machine storage ops [-h] [--op-id id] [-s [^]state] [--type type] [--since date|age] [--until date|age] [--limit n] [--offset n] [-w] |
| Option | Does |
|---|---|
--op-id id |
Show one operation, by its id (ignores -s and --type) |
-s, --state [^]state |
Filter by state: pending, done, failed (default: pending, failed; repeat = OR; ^ excludes) |
--type type |
Filter by kind: measure, prune (repeat = OR) |
--since date|age |
Since a date or age |
--until date|age |
Until a date or age |
--limit n |
Show at most n operations (default: 25) |
--offset n |
Skip the first n operations |
-w, --watch |
Watch for changes |
machine storage prune
Reclaim space held by deleted runs and disks. See Account and billing.
Reclaims space held by deleted runs and disks above 5%.
1 | usage: machine storage prune [-h] [-y] [--status] |
| Option | Does |
|---|---|
-y, --yes |
Do not ask for confirmation |
--status |
Show the current or last prune and exit |
machine artifacts
Browse and copy files from a run’s disk. See Reading the disk.
Lists, copies, or prints files from a run’s disk.
Reads are snapshot-pinned. They never perturb execution.
1 | usage: machine artifacts [-h] {ls,cp,cat} ... |
machine artifacts ls
List files in a run’s disk. See Reading the disk.
Lists files in a run’s disk.
Pin the listing to a checkpoint or entry with run@ref.
1 | usage: machine artifacts ls [-h] [-l] [-a] run[@ref] [path] |
| Argument | Does |
|---|---|
run[@ref] |
Run name/id, optionally @entry_id or @checkpoint |
path |
Path (default: /) |
| Option | Does |
|---|---|
-l |
Long format (mode, size, mtime, name) |
-a, --all |
Include . and .. |
machine artifacts cp
Copy a file or directory. See Reading the disk.
Copies a file or directory from a run’s disk.
Pin the copy to a checkpoint or entry with run@ref. Use -r for directories.
1 | usage: machine artifacts cp [-h] [-r] run[@ref] path [dest] |
| Argument | Does |
|---|---|
run[@ref] |
Run name/id, optionally @entry_id or @checkpoint |
path |
Path to copy |
dest |
Local destination file or directory (default: .) |
| Option | Does |
|---|---|
-r, -R, --recursive |
Copy directories recursively |
machine artifacts cat
Print a file to stdout. See Reading the disk.
Prints a file from a run’s disk.
Pin the read to a checkpoint or entry with run@ref.
1 | usage: machine artifacts cat [-h] run[@ref] path |
| Argument | Does |
|---|---|
run[@ref] |
Run name/id, optionally @entry_id or @checkpoint |
path |
File path |
machine ping
Check reachability and latency. See The client.
Checks reachability and latency.
Pings the API and connected sessions.
1 | usage: machine ping [-h] [-c n] |
| Option | Does |
|---|---|
-c, --count n |
Round-trips per target (default: 3) |
machine account
Manage account. See Account and billing.
Shows and manages the account.
1 | usage: machine account [-h] {devices,buy,upgrade,downgrade,payment,portal,invoices,cancel,email,products,show,credits,activity,orders,api-keys,provision,signup,close} ... |
machine account devices
List the devices signed in to the account. See Account and billing.
Lists signed-in devices.
Use --revoke to sign out one device by id.
1 | usage: machine account devices [-h] [--revoke id] |
| Option | Does |
|---|---|
--revoke id |
Sign out one device by its id (or a unique id prefix) |
machine account buy
Buy usage credits or a plan. See Account and billing.
Buys usage credits or a plan.
1 | usage: machine account buy [-h] {usage,plan} ... |
machine account buy usage
Buy usage credits. See Account and billing.
Buys usage credits.
Opens checkout for the named pack size.
1 | usage: machine account buy usage [-h] [-y] eur |
| Argument | Does |
|---|---|
eur |
Pack size in euros, as account products lists it |
| Option | Does |
|---|---|
-y, --yes |
Skip the confirmation prompt |
machine account buy plan
Subscribe to a plan. See Account and billing.
Subscribes to a plan.
Already on a paid plan upgrades or downgrades to the named tier instead.
1 | usage: machine account buy plan [-h] [-y] tier |
| Argument | Does |
|---|---|
tier |
Plan tier (e.g. starter, pro) |
| Option | Does |
|---|---|
-y, --yes |
Skip the confirmation prompt |
machine account upgrade
Upgrade to a higher plan. See Account and billing.
Upgrades to a higher plan.
An omitted tier is the next one up. Use --link to print the URL without
opening a browser.
1 | usage: machine account upgrade [-h] [--link] [-y] [tier] |
| Argument | Does |
|---|---|
tier |
Target tier (default: next up) |
| Option | Does |
|---|---|
--link |
Print the link only (don’t open a browser) |
-y, --yes |
Skip the confirmation prompt |
machine account downgrade
Downgrade to a lower plan. See Account and billing.
Downgrades to a lower plan.
An omitted tier is the next one down. Use --link to print the URL without
opening a browser.
1 | usage: machine account downgrade [-h] [--link] [-y] [tier] |
| Argument | Does |
|---|---|
tier |
Target tier (default: next down) |
| Option | Does |
|---|---|
--link |
Print the link only (don’t open a browser) |
-y, --yes |
Skip the confirmation prompt |
machine account payment
Update your payment method. See Account and billing.
Updates the payment method.
Use --link to print the URL without opening a browser.
1 | usage: machine account payment [-h] [--link] |
| Option | Does |
|---|---|
--link |
Print the link only (don’t open a browser) |
machine account portal
Open billing settings. See Account and billing.
Opens billing settings.
Use --link to print the URL without opening a browser.
1 | usage: machine account portal [-h] [--link] |
| Option | Does |
|---|---|
--link |
Print the link only (don’t open a browser) |
machine account invoices
List and manage invoices. See Account and billing.
Lists invoices.
The listing shows the newest 25. Use --limit for a different page size.
1 | usage: machine account invoices [-h] [--since date|age] [--until date|age] [--status {draft,open,paid,uncollectible,void}] [--limit n] [--sort field] [--reverse] {list,download} ... |
| Option | Does |
|---|---|
--since date|age |
Since a date or age |
--until date|age |
Until a date or age |
--status {draft,open,paid,uncollectible,void} |
Filter by invoice status |
--limit n |
Cap to most recent n (default: 25, max 100) |
--sort field |
Sort by created or amount |
--reverse |
Reverse sort order |
machine account invoices list
List invoices. See Account and billing.
Lists invoices.
The listing shows the newest 25. Use --limit for a different page size.
1 | usage: machine account invoices list [-h] [--since date|age] [--until date|age] [--status {draft,open,paid,uncollectible,void}] [--limit n] [--sort field] [--reverse] |
| Option | Does |
|---|---|
--since date|age |
Since a date or age |
--until date|age |
Until a date or age |
--status {draft,open,paid,uncollectible,void} |
Filter by invoice status |
--limit n |
Cap to most recent n (default: 25, max 100) |
--sort field |
Sort by created or amount |
--reverse |
Reverse sort order |
machine account invoices download
Open an invoice, the PDF and the receipt. See Account and billing.
Opens an invoice.
The hosted page has the PDF and receipt. Use --link to print the URL without
opening a browser.
1 | usage: machine account invoices download [-h] [--link] invoice-id |
| Argument | Does |
|---|---|
invoice-id |
Invoice id (in_…) |
| Option | Does |
|---|---|
--link |
Print the link only (don’t open a browser) |
machine account cancel
Cancel your subscription. See Account and billing.
Cancels the subscription.
The plan stays until renewal, then drops to Free. Usage credits stay. Use
--link to print the URL without opening a browser.
1 | usage: machine account cancel [-h] [--link] |
| Option | Does |
|---|---|
--link |
Print the link only (don’t open a browser) |
machine account email
Change your account email. See Account and billing.
Changes the account email.
A code goes to the current inbox. A link goes to the new one.
1 | usage: machine account email [-h] [-y] email |
| Argument | Does |
|---|---|
email |
New email address |
| Option | Does |
|---|---|
-y, --yes |
Skip confirmation prompts |
machine account products
List products for sale. See Account and billing.
Lists products for sale.
1 | usage: machine account products [-h] {list} ... |
machine account products list
List products. See Account and billing.
Lists products for sale.
1 | usage: machine account products list [-h] |
machine account show
Show your account. See Account and billing.
Shows the account.
1 | usage: machine account show [-h] |
machine account credits
Show your usage credits. See Account and billing.
Shows usage credits.
1 | usage: machine account credits [-h] |
machine account activity
Show usage credit activity. See Account and billing.
Shows usage credit activity.
The listing shows the newest 25. Use --limit for a different page size. Naming
a session focuses the listing on that session.
1 | usage: machine account activity [-h] [--since date|age] [--until date|age] [--period date] [--type {included,reserved,settled,expired,expired-removed,credit-purchase}] [--credit-type {included,usage}] [--limit n] [--sort field] [--reverse] |
| Argument | Does |
|---|---|
session |
Show one session’s transactions, by name, id or id prefix |
| Option | Does |
|---|---|
--since date|age |
Since a date or age |
--until date|age |
Until a date or age |
--period date |
Show credit activity in the period containing DATE |
--type {included,reserved,settled,expired,expired-removed,credit-purchase} |
Filter by activity type |
--credit-type {included,usage} |
Filter by credit type (included or usage) |
--limit n |
Cap to most recent n (default: 25, max 500) |
--sort field |
Sort by sequence or amount |
--reverse |
Reverse sort order |
machine account orders
List and manage purchases. See Account and billing.
Lists purchases.
The listing shows the newest 25. Use --limit for a different page size.
1 | usage: machine account orders [-h] [--status {pending,processing,completed,cancelled,expired,failed}] [--limit n] {list,resume,cancel} ... |
| Option | Does |
|---|---|
--status {pending,processing,completed,cancelled,expired,failed} |
Filter by status |
--limit n |
Cap to most recent n (default: 25, max 200) |
machine account orders list
List orders. See Account and billing.
Lists purchases.
The listing shows the newest 25. Use --limit for a different page size.
1 | usage: machine account orders list [-h] [--status {pending,processing,completed,cancelled,expired,failed}] [--limit n] |
| Option | Does |
|---|---|
--status {pending,processing,completed,cancelled,expired,failed} |
Filter by status |
--limit n |
Cap to most recent n (default: 25, max 200) |
machine account orders resume
Resume a pending checkout. See Account and billing.
Resumes a pending checkout.
Defaults to the single pending order. Use --link to print the URL without
opening a browser.
1 | usage: machine account orders resume [-h] [--link] |
| Option | Does |
|---|---|
--link |
Print the link only (don’t open a browser) |
machine account orders cancel
Cancel a pending order so you can buy again. See Account and billing.
Cancels a pending order so another buy can start.
Defaults to the single pending order.
1 | usage: machine account orders cancel [-h] |
machine account api-keys
List and manage API keys. See Account and billing.
Lists and manages API keys.
Keys can run machines, disks, and sessions, not billing or account changes.
1 | usage: machine account api-keys [-h] {list,create,delete,update} ... |
machine account api-keys list
List API keys. See Account and billing.
Lists API keys.
1 | usage: machine account api-keys list [-h] |
machine account api-keys create
Create an API key. See Account and billing.
Creates an API key.
The secret is shown once. A sign-in code is required.
1 | usage: machine account api-keys create [-h] [--expiry days|never] name |
| Argument | Does |
|---|---|
name |
Label to recognize this key later |
| Option | Does |
|---|---|
--expiry days|never |
Expire after N days (e.g. 30), or “never” (default: never) |
machine account api-keys delete
Delete an API key. See Account and billing.
Deletes an API key.
A sign-in code is required.
1 | usage: machine account api-keys delete [-h] id |
| Argument | Does |
|---|---|
id |
Key id, as account api-keys lists it |
machine account api-keys update
Set an API key’s expiry. See Account and billing.
Sets an API key’s expiry.
A sign-in code is required.
1 | usage: machine account api-keys update [-h] id days|never |
| Argument | Does |
|---|---|
id |
Key id, as account api-keys lists it |
days|never |
Number of days from now (e.g. 30) or “never” |
machine account provision
Set up account storage. See Signing up.
Sets up cloud storage.
Safe to repeat while it is still working.
1 | usage: machine account provision [-h] |
machine account signup
Create an account. See Signing up.
Creates an account.
A link is emailed to finish signing up.
1 | usage: machine account signup [-h] |
machine account close
Close your account. See Account and billing.
Closes the account.
Two steps: start, then confirm after machines stop. Use --cancel to abort a
close already in progress.
1 | usage: machine account close [-h] [-y] [--cancel] |
| Option | Does |
|---|---|
-y, --yes |
Skip confirmation prompts |
--cancel |
Abort closing and return the account to active |
machine login
Sign in. See Signing up.
Signs in.
A sign-in code is emailed. Use account signup if there is no account yet.
1 | usage: machine login [-h] |
machine logout
Sign out. See Signing up.
Signs out.
Revokes the access token on the server. Use --everywhere to sign out of every
device and browser.
1 | usage: machine logout [-h] [--everywhere] |
| Option | Does |
|---|---|
--everywhere |
Sign out of every device and browser, not just this one. |
Python SDK
The SDK does what the client does, from Python. If you can type it in a terminal, you can call it from a program.
Installing
1 | pip3 install testflows.machine |
It needs Python 3.11 or later on Linux x86_64, the same systems as the client. If
you need another platform, contact us. pip installs
the Machine core that the SDK runs on, testflows.machine.core, along with it.
Hello Machine
1 | import testflows.machine.v1 as machine |
This creates a session, boots a run from the disk hello and prints the last
twenty lines of its console. A run waits for commands, so it moves only when
you call run.run(...); pass daemon=False to have it run to the end by
itself.
Leaving a with block deletes what it created, whether the block ends normally
or with an error. That is how you make sure a session never outlives your
program.
It looks like the client
Every client command has a matching SDK call, and the names follow a simple rule.
| In the client | In the SDK |
|---|---|
a top-level command, machine create --disk app |
a method on the client, client.create(disk="app") |
a group, machine sessions use ci-1 |
an attribute of the client, client.sessions.use("ci-1") |
a command that takes a run, machine fork app --at ^boot |
a method on the run, run.fork(at="^boot") |
a group that takes a run, machine tasks app hold 56 |
an attribute of the run, run.tasks.hold(56) |
| a positional argument | a positional argument |
a flag like --no-wait, -w or -f |
a keyword argument like wait=False, watch=True or follow=True |
So once you know the client, you know most of the SDK. Commands
lists them, and the client’s --help says what each call takes.
Signing in
The SDK signs in with an API key from TESTFLOWS_MACHINE_API_KEY if it is set.
If it isn’t, it uses the login that machine login stored, which it shares
with the client.
A program can sign in on its own too. The one-time code arrives by email.
1 | machine.login.start("me@example.com") |
machine.login.logout() signs out.
Sessions
A client has a current session, like the CLI does. You can set it when you create the client, or later.
1 | client = machine.Client(session="ci-1") # like -s ci-1 |
sessions.use only changes the session for this client. It never changes it for
the machine command. Without a session you can still read your account’s runs:
client.runs(), client.branches() and a run’s describe(), lineage()
and checkpoints() all work.
Runs
Here is a typical run, from creating it to branching and replaying.
1 | run = client.create("app", disk="app", rng=True) |
And the calls you will reach for most often:
| Call | Does |
|---|---|
client.create(name, disk=..., cpus=..., ...) |
creates a run and boots it; takes every option machine create does |
client.runs(), client.runs["app"] |
lists runs, or finds one by name |
run.run(until=..., iters=...), run.plan(file) |
drives it |
run.checkpoint(name), run.checkpoints() |
saves a checkpoint, lists them |
run.fork(...), run.go(point), run.switch(branch), run.detach(...) |
branches |
run.replay(), run.play(...), run.diff(...) |
replays and compares |
run.describe(), run.state(), run.now(), run.entries(...) |
reads it |
run.console(...) |
reads what it printed |
run.artifacts.ls(path, at=...), .cat(...), .cp(...) |
reads its disk |
run.tasks(), run.focus.add(...), run.irq(...), run.vtime.rate(...) |
steers the machine |
run.start(), run.stop(), run.pause(), run.kill(), run.delete() |
lifecycle |
Every call returns a record with named fields, such as a RunInfo from
describe() or a list of Task from tasks(). Call as_json() on one to get
a plain dict.
Watching and following
A listing takes watch=True and then gives you a new list every time
something changes. A log takes follow=True and gives you new lines as they
come, like tail -f.
1 | for runs in client.runs(states=["running"], watch=True, timeout=60): |
Choosing entries
Anything that reads entries takes the same few spellings.
| You write | Means |
|---|---|
200 or (100, 200) |
that entry, or that range, both ends included |
"100:200", "100:", ":200" |
a range, from 100 on, or up to 200 |
"-20" |
the last 20 entries |
slice(100, 200) |
a Python slice, so 100 through 199 |
Be careful with the last row. A pair and a string include both ends, the way
Machine’s own messages do. A slice is Python’s, so it stops before the end.
That makes (100, 200) one entry longer than slice(100, 200).
lines= selects lines of output in the same grammar: 20 is line 20, -20
the last 20, and 0 none, which starts a follow at the end.
Cleaning up
A session costs from the moment you create it, and a run keeps its log and checkpoints until you delete it. Both can be used as context managers, as in the first example, and deleting a run takes its branches with it.
If you want to keep a run that found something, don’t wrap it in a with
block. Nothing is deleted until you call delete().
Errors
Every exception the SDK raises is a machine.Error, and each one says what
went wrong.
| Exception | Raised when |
|---|---|
NotSignedIn |
there are no credentials |
Unauthorized |
a key or a sign-in code was rejected |
Forbidden |
your account is not allowed to do that |
NotFound |
nothing matches the name or id |
AlreadyExists |
something with that name exists |
InvalidArgument |
an argument is wrong, or a name matches more than one thing |
NotRunning |
it exists, but it is not running |
Conflict |
the request does not fit the thing’s current state |
Diverged |
a replay did not reproduce what was recorded |
Timeout |
a wait ran out of time |
ServiceUnavailable |
the service could not be reached |
ApiError |
the service refused the request |
1 | try: |
Examples
The SDK comes with small scripts. Each one runs as
python <script> --disk <name>, creates its own session, and deletes it at the end.
| Script | Shows |
|---|---|
hello.py |
creates a run, boots it, reads its console |
checkpoint_and_fork.py |
a checkpoint after boot, and branches from it |
replay_verify.py |
replays a recording and names what diverged |
explore_seeds.py |
one branch per seed, with the failing ones marked and kept |
force_interleaving.py |
holds one thread while another runs past it |
plan_file.py |
drives a run with when and else clauses |
watch_and_follow.py |
watches a listing and follows a console |
syscall_profile.py |
counts a program’s system calls and interrupts |
read_artifacts.py |
reads and copies files from a checkpoint’s disk |
machine_time.py |
changes the machine’s clock (vtime) rate and pushes it forward |
inject_interrupt.py |
injects an interrupt and replays it |
stop_and_resume.py |
what stop, kill, start and sync keep |
resume_elsewhere.py |
stops a run in one session and starts it in another |
ci_job.py |
a CI job with an API key, a session per job and an exit code |
error_handling.py |
every error and how to recover from it |
License
The SDK is licensed under Apache 2.0. The Machine core it runs on,
testflows.machine.core, is not part of it and has its own license.