Core concepts¶
This page explains the key abstractions that make sdb tick. Understanding these concepts will help you read sdb output, compose pipelines effectively, and write your own commands.
The pipeline¶
Every sdb expression is a pipeline of one or more commands separated by
|. Data flows left to right as a stream of drgn.Object values:
┌──────────┐ ┌────────┐ ┌────────┐
│ Locator │────▶│ Filter │────▶│ Pretty │
│ (source) │ │ │ │ Printer│
└──────────┘ └────────┘ └────────┘
The first command in a pipeline either:
receives no input and produces objects on its own (a Locator), or
receives objects from a manual source (
echo,addr).
The last command either:
pretty-prints its input (if it is a PrettyPrinter), or
formats each object using drgn’s default
format_()method.
Middle commands transform, filter, or walk the stream.
A pipeline can optionally end with ! <shell command> to redirect sdb’s
printed output into a Unix process:
sdb> stacks | count ! tee /tmp/count.txt
Automatic type coercion¶
When a command declares an input_type of foo_t *, sdb automatically
handles two common mismatches:
void * → foo_t *: If the previous command yields
void *objects, sdb inserts an implicitcast foo_t *step.foo_t → foo_t *: If the previous command yields
foo_t(the struct, not a pointer to it), sdb inserts an implicitaddressstep to take its address.
This means you rarely need to cast manually – just pipe things together and sdb will do the right thing.
Command types¶
Every sdb command inherits from sdb.Command. Several specialised
subclasses exist, each with a different contract:
Command¶
The generic base class. Subclasses implement:
def _call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]:
...
The method receives objects from the previous pipeline stage and yields objects to the next. It may also print output as a side effect.
SingleInputCommand¶
Like Command, but processes each input object independently. Subclasses
implement:
def _call_one(self, obj: drgn.Object) -> Iterable[drgn.Object]:
...
If one object triggers a drgn.FaultError (invalid memory), sdb prints
the error and continues with the next object instead of aborting the entire
pipeline. This is useful for commands that iterate over potentially stale
data structures.
Walker¶
A command that iterates over a container data structure. Subclasses declare
input_type (e.g. "struct list_head *") and implement:
def walk(self, obj: drgn.Object) -> Iterable[drgn.Object]:
...
Walkers are registered in a global table keyed by their input_type.
The walk dispatch command looks up the right walker automatically:
sdb> addr slab_caches | walk
To see all registered walkers, run walk with no input:
sdb> walk
The following types have walkers:
WALKER TYPE
lxlist struct list_head *
avl avl_tree_t *
...
PrettyPrinter¶
A command that produces human-readable output for a specific type.
Subclasses declare input_type and implement:
def pretty_print(self, objs: Iterable[drgn.Object]) -> None:
...
When a PrettyPrinter is the last command in a pipeline, sdb calls
pretty_print(). When it is not last, it passes its input through
unchanged so downstream commands can continue processing.
The pp (pretty-print) command is a generic dispatcher – it checks if a
PrettyPrinter exists for the input type and delegates to it.
Locator¶
A command that finds objects of a given type. Subclasses declare
output_type (the type being located) and implement:
def no_input(self) -> Iterable[drgn.Object]:
...
no_input() is called when the Locator is the first command in a
pipeline (i.e. it has no upstream input). It should yield all objects of
the located type.
A Locator can also accept input. When it receives objects, it dispatches
to type-specific handlers registered with the @InputHandler decorator
(see Developer notes). If no specific handler matches, the Locator
tries to use a Walker to iterate the input and cast each element to
output_type.
Many built-in commands (spa, stacks, threads, slabs, etc.)
are both a Locator and a PrettyPrinter. When used to start a pipeline
they locate objects; when used at the end they pretty-print them; and in the
middle they pass objects through.
Runtimes¶
Commands declare when they should be loaded using the load_on attribute:
Runtime |
When the command is registered |
|---|---|
|
Always (e.g. |
|
Only when debugging a kernel target. |
|
Only when debugging a userland process. |
|
Currently equivalent to |
|
Currently equivalent to |
This prevents kernel-only commands from appearing when you are debugging a userland core, and vice versa.
echo and addr – entering the pipeline¶
Two built-in commands let you inject objects into a pipeline:
echo(aliascc)Creates a
void *from a raw numeric address. Useful when you have an address from a log or another tool:sdb> echo 0xffff9d0adbe2c000
addr(aliasaddress)Resolves a symbol name or hex address to a drgn object, preserving type information:
sdb> addr jiffies *(volatile unsigned long *)0xffffffff97205000 = 4901248625
addrcan take multiple arguments:sdb> addr jiffies slab_caches 0xffffdeadbeef
The difference: echo always produces void *; addr produces a
typed pointer when given a symbol name. In practice, addr is more
commonly used because you get type information for free.
Casting¶
Explicit casting with the cast command:
sdb> echo 0xffff9d0adbe2c000 | cast spa_t *
cast accepts any C type expression that drgn can resolve, including
typedefs, qualified types, and pointers to pointers. It uses
drgn.cast() under the hood.
As described in Automatic type coercion, most casts happen implicitly
when piping void * objects into a command that declares an
input_type.