summaryrefslogtreecommitdiffstats
path: root/docs/05-operations/02-debugging-and-maintenance-workflows.md
blob: 331597a798888e9575980b246ed93a5b6b895c7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# Debugging and Maintenance Workflows

> The practical book: how to see what is happening, how to find the cause, and
> how to add a new firmware, driver, hypervisor or hardware version without
> breaking anything.

## 1. The observability model

Three layers, in order of usefulness:

1. **Guest diagnostics** — the guest driver is the best oracle. It prints
   precisely what it expected and what it got (e.g. UVM's "Tracker entry for
   value N … channel_init()"). Always start here.
2. **Host module trace** — every RPC and every emulation decision is logged
   with the `gsp-resman-vfio:` prefix. A full CUDA run produces a few hundred
   lines that reconstruct the object tree and the control battery.
3. **Memory observation** — when the problem is in memory state (mappings,
   semaphores, pushes), the module's debug parameters read and write guest
   physical memory directly.

## 2. Standard captures for a bug report

```bash
# Host
sudo dmesg -T | grep -iE 'gsp-resman-vfio|gsp_|nvidia|vfio|mdev' > host.log
./scripts/stack_manager.sh status > status.log
# Guest
sudo dmesg > guest-dmesg.log                 # include full dmesg
sudo lspci -vvv -d 10de: > guest-lspci.log   # config space of the virtual GPU
cat /proc/cmdline > guest-cmdline.log
nvidia-smi -q > nvidia-smi-q.log
```

Plus: the emulated driver profile (GPU type, firmware image hash, driver
version) and the exact command sequence used. `tools/guest-diag/push_guest_tools.sh`
collects some of this automatically.

## 3. Live debugging toolbox

### 3.1 Kernel logs

```bash
sudo dmesg -w | grep -iE 'gsp-resman-vfio'          # live emulation trace
sudo dmesg -T | grep gsp-resman-vfio | tail -100    # recent
# capture to a file when the ring buffer is small and traffic is heavy:
sudo stdbuf -oL dmesg -w > run.log
```

### 3.2 Module debug parameters

See the host-module doc for the full list. Canonical examples:

```bash
# Find where a value lives in guest RAM (e.g. a semaphore VA):
echo 0x121000000 | sudo tee /sys/module/open_iov_vfio/parameters/scan_guest_ram

# Find a decoded method sequence in guest RAM:
echo 0x1,0x21000000,0x1 | sudo tee /sys/module/open_iov_vfio/parameters/scan_seq

# Write a value to a guest physical address (completing a semaphore release):
echo 0x120299010,0x1 | sudo tee /sys/module/open_iov_vfio/parameters/poke_guest
```

**Caution:** all guest-RAM operations run in a kernel thread context by design;
calling them from a normal process context fails with `-EFAULT` (the
hypervisor's DMA interface requires either the mapping's `mm` or no `mm` at
all). This is not a bug.

### 3.3 QEMU gdbstub (guest virtual memory)

The reference QEMU configuration exposes a gdbstub (`-gdb tcp::1234`). It can
read guest *virtual* memory through the vCPU's page tables and write to it —
the single most powerful trick we have when the guest's own kernel address
matters:

```bash
gdb -batch -ex 'target remote :1234' \
    -ex 'x/8wx 0xffff...' -ex 'set *(unsigned int*)0xffff... = 1'
```

Workflow that made the memory model tractable:

1. Find the guest's `page_offset_base` (direct-map base) from
   `/proc/kallsyms` in the guest.
2. Convert a kernel CPU VA to a physical address: `PA = VA - page_offset_base`.
3. Use `poke_guest` to write the physical address from the host module, or use
   the gdbstub to write the virtual address directly.

### 3.4 Guest-side tracing

* `ptrace` tracer (`tools/guest-diag/ioctl_trace.c`): libcuda issues raw
  syscalls, so `LD_PRELOAD` shims see nothing; PTRACE_SYSCALL with
  `PTRACE_GET_SYSCALL_INFO` does.
* UVM debug: `echo 1 > /sys/module/nvidia_uvm/parameters/uvm_debug_prints`
  (messages appear as `nvidia-uvm:`).
* kprobes do not work on NVIDIA text (`notrace`); do not waste time on them.

### 3.5 Failure decision trees

When a failure is understood, write the decision tree into this document tree
— the tree is what makes the next failure cheap. The field guide's tables are
the result of that discipline so far.

### 3.6 Userspace driver (`libcuda`) crash workflow

When the guest segfaults inside the userspace driver, the fault is reported as
`lib[base+size] ip`. Resolve it and find the code before reading any
decompilation:

```bash
# ip/mapping-base decoder: prints the file vaddr, containing function start,
# and nearest export
userspace/cuda/tools/cudatool.py crash <libcuda.so> <mapping_base> <ip>

# decompile exactly one file vaddr (translates for the Ghidra image base)
userspace/cuda/tools/ghidra_decompile_at.sh <ghidra-project> <program> <libcuda.so> 0xNNNNNN /tmp/fn.c

# callers/callees of an address in the annotated corpus
userspace/cuda/tools/callwalk.py <elf-index>/callgraph.tsv callers 0xNNNNNN
```

* **Address convention**: public offsets are *file virtual addresses*
  (`libcuda+0x...`). Ghidra loads the CUDA userspace library at **image base
  `0x100000`**, so addresses in old decompilations produced without that
  translation are 0x100000 too low (the wrong functions); runtime
  observations from those sessions remain valid, but conclusions drawn from
  their decompiled code must be re-derived. The current tooling translates
  for you.
* The annotated corpus (layout in
  [`../../userspace/cuda/READ-ME-FIRST.md` §4](../../userspace/cuda/READ-ME-FIRST.md))
  is searchable by `libcuda+0x...`; names/indexes are our own work product.
* **Engine topology**: `tools/guest-diag/guest_engines.c` dumps the guest's
  engine/class list, virtualization mode and CE caps; compare against the
  host oracle (the same query on the host driver) before blaming the
  emulation.
* **A/B harness**: `tools/cuda-ab/` runs one deterministic CUDA workload on
  bare metal and in the guest and diffs the stable result lines.

**Known current bugs (2026-09-23)**: `cuCtxCreate` returns `rc=999`
(`CUDA_ERROR_UNKNOWN`) because the emulated engine list is missing the async
copy engine `COPY14`; the guest kernel oopses in
`uvm_pmm_devmem_page_to_va_space` on teardown of the failed context; and
control `0x2080012b` (560-byte params, issued immediately before context
creation) is answered with a generic SUCCESS whose consumed fields are not yet
coherent. Start at
[`../../userspace/cuda/READ-ME-FIRST.md`](../../userspace/cuda/READ-ME-FIRST.md),
[`../../userspace/cuda/docs/03-driver-and-gpu-interface.md`](../../userspace/cuda/docs/03-driver-and-gpu-interface.md)
and
[`../../userspace/cuda/docs/05-570.144-decompilation.md`](../../userspace/cuda/docs/05-570.144-decompilation.md).

## 4. Workflows for new inputs

### 4.1 New firmware version

1. Add the image under `firmware/<SERIES>-GSP/<VERSION>/` (local study
   material; firmware blobs are gitignored and not distributed in the
   repository).
2. Compute signatures (CRC32/SHA256) with the firmware analyzer.
3. Add a firmware profile entry (addresses/signatures) so runtime analysis can
   identify it.
4. Run the driver against a guest using that firmware's matching driver; diff
   the RPC/control inventory against the baseline.
5. Add/adjust handlers only where the diff requires it; re-run the ladder.

### 4.2 New guest driver version

1. Install the stock driver in a guest (no patches).
2. Capture the full init trace and diff function/control IDs against the
   previous version.
3. For each difference: decode against the exact-version reference headers.
4. Add a version-keyed handler or a capability table entry.
5. Re-run rungs 1–2; then the CUDA corpus.

### 4.3 New hardware generation

1. Add a hardware profile (PCI IDs, BAR layout, memory geometry, engine list).
2. Run the ladder on that hardware with the closest driver family.
3. Fix differences by configuration first; only add code when an interface
   genuinely differs.
4. Publish the profile and the test evidence.

### 4.4 New hypervisor

1. Implement the transport boundary (device registration, guest-memory access,
   interrupt injection) — the boundary is small by design.
2. Re-run the ladder; the emulation core must not need changes.
3. Document any hypervisor-specific quirk in the debugging docs.

## 5. Crash and hang recovery

| Situation | Action |
|---|---|
| Module stuck unloading | `stack_manager.sh cleanup-crash`; reboot if it persists (request approval per project policy) |
| VM wedged, guest filesystem dirty | stop the VM gracefully (`scripts/vgpu_vm.py stop`), fsck via `qemu-nbd` only if needed |
| Guest driver wedged (D-state processes) | reboot the guest; capture dmesg first |
| Kernel oops in the host module | capture the full oops + `dmesg` before reloading |

## 6. Release engineering

* Versions are tagged; each tag carries the rung/matrix table and the
  performance table for the platforms tested.
* Configuration profiles are versioned with the code; a profile bump is a
  reviewed change with evidence.
* The docs in this tree are part of the release: a release that changes
  behaviour without updating them is incomplete.