> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lagerdata.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Diagnose

> Single-shot diagnosis for a misbehaving instrument net

`lager diagnose <net> --box <box> [--type <role>]` is a single-shot diagnosis for
a misbehaving instrument net. It collapses the manual debug workflow (`lsof`,
`dmesg`, bare `pyvisa` probes, hardware-service introspection) into one CLI call
that returns an actionable classification — host-side, instrument-wedged, or
healthy.

<Note>Introduced in **lager 0.20.0** for USB-TMC (pyvisa) instruments. Extended in
**0.28.3** to diagnose `debug` nets (SEGGER J-Link, and a basic OpenOCD/ST-Link
path) — see [Debug nets (J-Link)](#debug-nets-j-link) below.</Note>

## Syntax

```bash theme={null}
lager diagnose NET [OPTIONS]
```

## Options

| Option        | Description                                                                                                                                                                                                                                                                                    |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--box BOX`   | Lagerbox name or IP address (uses the default box if omitted)                                                                                                                                                                                                                                  |
| `--type ROLE` | Net role. Defaults to `auto`, which looks up the net's role from the box's saved nets. Pass an explicit role (`battery`, `power-supply`, `scope`, `debug`, `usb`, `adc`, …) to override, or to diagnose a net that isn't saved. A `debug` net routes to the [J-Link path](#debug-nets-j-link). |

`NET` is the name of the net to diagnose (e.g. `battery1`, `supply1`).

***

## Usage

```bash theme={null}
# Diagnose a net, auto-detecting its role from saved nets
lager diagnose battery1 --box my-lager-box

# Override the role explicitly
lager diagnose battery1 --box my-lager-box --type battery
```

The command queries three box-side endpoints in parallel and prints a section for
each, followed by a one-line classification with the next step.

***

## Output sections

### USB (host-side)

From `GET /diagnose/usb` on box port 9000. Reports:

* `enumerated` — does the device show up on the host's USB bus?
* `sysfs` — kernel sysfs path (e.g. `/sys/bus/usb/devices/1-4`).
* `device` — `/dev/bus/usb/BBB/DDD` path used by `lsof`/`fuser`.
* `usbtmc` — whether the `usbtmc` kernel module is loaded (and therefore racing
  libusb for interface 0).
* `lsof` — `command(pid)` list of processes holding the USB device file.
* `dmesg tail` — last few USB / usbtmc kernel messages.

### VISA (instrument-side)

From `GET /diagnose/visa` on box port 9000. Opens a *fresh* `pyvisa` session and
queries `*IDN?` with a short timeout. Skips the open (with a clear note) if the
hardware service already holds a shared session for this address — collisions
would either hang or return garbage. Reports:

* `idn` — the IDN string if the instrument answered.
* `elapsed` — wall-clock ms.
* `error` / `error_class` — classified as `busy`, `nodev`, `timeout`, or `other`.
* `skipped` — set when the hardware service holds the address.

### Dispatcher (hw\_service in-process)

From `GET /diagnose/dispatcher` on hardware-service port 8080. Reports the
in-process state for this address:

* `cached_session` — whether the shared `pyvisa` session pool has it.
* `cached_drivers` — driver instances cached against this address.
* `shared_pool` — total pool size.

***

## Classifications

The decision tree, in order (first match wins):

| Classification                                           | Trigger                                                                           |
| -------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `HOST-SIDE: usbtmc kernel module loaded`                 | the `usbtmc` kernel module is bound (run `lager update` to install the blacklist) |
| `HOST-SIDE: USB device claimed by multiple processes`    | VISA `busy` and two or more holders in `lsof`                                     |
| `HOST-SIDE: USB device busy`                             | VISA `busy` with a single holder                                                  |
| `TRANSIENT: device disappeared from USB`                 | VISA `nodev` (re-enumeration)                                                     |
| `INSTRUMENT WEDGED`                                      | VISA `timeout` — enumerates and opens, but won't answer `*IDN?`                   |
| `NOT ENUMERATED`                                         | the device does not show up on USB (check power/cable)                            |
| `HEALTHY`                                                | `*IDN?` returned (IDN string shown)                                               |
| `HEALTHY (shared session)`                               | the open was skipped because hw\_service holds an active session                  |
| `TRANSIENT: enumerated as USB-TMC but fresh open failed` | USB-TMC class but the fresh pyvisa open failed                                    |
| `NOT USB-TMC`                                            | a vendor-SDK instrument (LabJack/LJM, Picoscope, Acroname, …), not pyvisa         |
| `UNCLEAR`                                                | fallback — review the per-section output                                          |

***

## Sample session

```
$ lager diagnose battery1 --box my-lager-box
lager diagnose — my-lager-box → battery1
  NetType: battery    address: USB0::0x05E6::0x2281::4518305::INSTR

== USB (host-side) ==
   enumerated:   True
   usbtmc kmod:  not loaded (good)
   lsof:         no holders

== VISA (instrument-side) ==
   idn:          KEITHLEY INSTRUMENTS,MODEL 2281S-20-6,4518305,01.08b
   elapsed:      429 ms

== Dispatcher (hw_service in-process) ==
   cached_session:  False
   shared_pool:     0 entry/entries

Classification: HEALTHY — IDN: KEITHLEY INSTRUMENTS,MODEL 2281S-20-6,4518305,01.08b
```

A wedged instrument surfaces clearly so you stop trying software-only recoveries:

```
Classification: INSTRUMENT WEDGED: device enumerates and accepts session open,
but won't respond to *IDN?. The instrument firmware is stuck — a mains-side
power-cycle of the instrument itself is required.
```

Vendor-SDK instruments (LabJack, Picoscope, Acroname) don't go through pyvisa, so
`lager diagnose` points you at the role-specific command instead of returning a
misleading `UNCLEAR`.

***

## Debug nets (J-Link)

A `debug` net isn't USB-TMC, so the pyvisa `*IDN?` probe above can't reach it.
When the net's role is `debug` (auto-detected, or forced with `--type debug`),
`lager diagnose` takes a J-Link-aware path instead: it fetches the same
host-side **USB** section plus a dedicated `/diagnose/jlink` endpoint and walks
the debug stack outside-in — software → USB → probe-visible → gdbserver →
target connect — so the most specific actionable fault wins.

```bash theme={null}
lager diagnose swd1 --box lab-lager-box            # auto-detects the debug role
lager diagnose swd1 --box lab-lager-box --type debug
```

### J-Link / debug probe section

In addition to the **USB (host-side)** section, a debug net prints a
**J-Link / debug probe** section reporting:

* `backend` — the probe backend (`jlink`, or an OpenOCD/ST-Link backend).
* `jlink software` — whether the SEGGER J-Link tools are installed on the box.
* `probe enum` — does the probe show up on the host's USB bus?
* `probe visible` — does `JLinkExe` actually enumerate the probe (with the
  emulator product/serial list)?
* `holders` — `command(pid)` of any process holding the probe (usually a stale
  gdbserver).
* `gdbserver` — whether a J-Link gdbserver is running, its PID, and whether its
  logfile looks healthy.
* `connect` — the result of a target-connect probe: `connect_ok`, an error
  class, `VTref` (target reference voltage), and the detected `core`. The raw
  `JLinkExe` output is shown when the failure can't be classified.

A SEGGER probe gets the full stack above. A non-J-Link OpenOCD/ST-Link probe
reports a lighter `openocd-basic` section (backend, probe enumeration, and
gdbserver state) — deep target diagnosis is J-Link-only for now.

### Debug classifications

| Classification                          | Trigger                                                                                                                                   |
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `HEALTHY: J-Link connected to <device>` | target connect succeeded (core, and VTref when J-Link reports it)                                                                         |
| `HEALTHY: J-Link gdbserver running`     | a gdbserver is up, listening, and its log is clean (active debug session)                                                                 |
| `J-LINK SOFTWARE MISSING on box`        | the SEGGER J-Link tools aren't installed (`lager update` installs them)                                                                   |
| `PROBE NOT ON USB`                      | the probe isn't enumerated — check cable, probe power, upstream hub port                                                                  |
| `PROBE CLAIMED`                         | probe is on USB but `JLinkExe` can't see it because another process holds it (usually a stale gdbserver — `lager debug <net> disconnect`) |
| `PROBE WEDGED`                          | probe is on USB but `JLinkExe` enumeration is empty — power-cycle the probe                                                               |
| `GDBSERVER WEDGED`                      | server process is up but its log shows a target-connection failure                                                                        |
| `TARGET UNPOWERED`                      | probe is fine but VTref is too low — the target board has no power on the debug header (or VTref isn't wired)                             |
| `TARGET LOCKED`                         | debug access is blocked by readout/IDCODE/AP protection — a mass-erase/unlock is required (e.g. `nrfjprog --recover`)                     |
| `DEVICE NAME`                           | `JLinkExe` rejected the configured device — fix the net's device/MCU field                                                                |
| `NO TARGET COMMS`                       | probe + target power OK but SWD/JTAG connect failed — check SWDIO/SWCLK wiring, nRST pull-up, SWD-vs-JTAG, try a lower speed              |
| `INCONCLUSIVE` / `UNCLEAR`              | connect probe was skipped or returned an unrecognized class — review the section output and rerun                                         |

### Sample debug session

```
$ lager diagnose swd1 --box lab-lager-box --type debug
lager diagnose — lab-lager-box → swd1
  NetType: debug    address: 50105878

== USB (host-side) ==
   enumerated:   True
   usbtmc kmod:  not loaded (good)
   lsof:         no holders

== J-Link / debug probe ==
   backend:        jlink
   jlink software: installed
   probe enum:     True
   probe visible:  True  (emus: J-Link/50105878)
   holders:        none
   gdbserver:      running=False pid=None log_ok=None
   connect:        ok=True class=ok VTref=3.300V core=Cortex-M4

Classification: HEALTHY: J-Link connected to NRF52840_XXAA (Cortex-M4, VTref=3.300V).
```

A locked target surfaces clearly so you reach for the right recovery:

```
Classification: TARGET LOCKED: debug access is blocked by readout/IDCODE/AP
protection. A mass-erase/unlock is required (e.g. `nrfjprog --recover` for nRF,
or the vendor unlock flow).
```

***

## Backwards compatibility

Against a pre-0.20 box, each endpoint returns 404 and the CLI notes that the
section is unavailable (the box may be on a lager \< 0.20 image). The remaining
sections still run — `lager diagnose` is useful against an older box, just less
informative.

***

## See Also

* [Instruments](/source/reference/cli/instruments) — list attached instruments and their VISA addresses
* [Nets](/source/reference/cli/nets) — list saved nets and their roles
* [Debug](/source/reference/cli/debug) — connect, flash, and gdbserver control for debug nets
* [Hello](/source/reference/cli/hello) — basic box-side connectivity and version check
