343 lines
13 KiB
Markdown
343 lines
13 KiB
Markdown
# Kontenery i debug
|
||
|
||
Ten dokument opisuje wdrożony kontrakt kontenerów i jawnie wskazuje pozostałe
|
||
etapy. Pełne
|
||
uzasadnienie, przepływ ucznia i kolejność wdrożenia są w
|
||
`doc/architecture-plan.md`.
|
||
|
||
Stan obecny:
|
||
|
||
- `stemctl` uruchamia trzy profile przez rootless Podman;
|
||
- repo środowiska zawiera wieloetapowy `Dockerfile` i Compose z dokładnie
|
||
trzema usługami; gotowych obrazów nie publikujemy;
|
||
- profil RP2350 zawiera oba toolchainy, Pico SDK, FreeRTOS, OpenOCD i picotool;
|
||
- `rvctl`, `host` i `rv32i` pozostają aliasami okresu zgodności.
|
||
|
||
## Profile docelowe
|
||
|
||
| Profil | Alias przejściowy | Service | Target Dockerfile | Targety |
|
||
| --- | --- | --- | --- | --- |
|
||
| `native-amd64` | `host` | `native-amd64` | `native-amd64-final` | `native` |
|
||
| `hazard3-sim` | `rv32i` | `hazard3-sim` | `hazard3-sim-final` | `hazard3-baremetal`; `hazard3-freertos` po BSP timer/IRQ |
|
||
| `rp2350` | brak | `rp2350` | `rp2350-final` | `rp2350-rv`, `rp2350-arm` |
|
||
|
||
Wszystkie targety dziedziczą stage `dev-ui-base`. Nie jest on osobnym
|
||
kontenerem roboczym.
|
||
|
||
## Jednolity kontrakt CLI
|
||
|
||
```bash
|
||
stemctl env list
|
||
stemctl env ensure native-amd64
|
||
stemctl env ensure hazard3-sim
|
||
stemctl env ensure rp2350
|
||
|
||
stemctl build native-amd64 inf bss 1
|
||
stemctl test native-amd64 inf bss 1
|
||
stemctl run native-amd64 inf bss 1
|
||
stemctl debug native-amd64 inf bss 1
|
||
|
||
stemctl build hazard3-sim inf bss 1 --target hazard3-baremetal
|
||
stemctl test hazard3-sim inf bss 1 --target hazard3-baremetal
|
||
stemctl run hazard3-sim inf bss 1
|
||
stemctl debug hazard3-sim inf bss 1
|
||
|
||
stemctl build rp2350 inf bss 1 --target rp2350-rv
|
||
stemctl deploy rp2350 inf bss 1 --target rp2350-rv --device /dev/bus/usb/001/006
|
||
stemctl debug rp2350 inf bss 1 --target rp2350-rv --device /dev/bus/usb/001/006
|
||
```
|
||
|
||
Selektory `series card task` są opcjonalne, jeśli ustawiono wartości domyślne.
|
||
Każda akcja obsługuje:
|
||
|
||
```text
|
||
--instance NAME
|
||
--target NAME
|
||
--editor nvim|vim
|
||
--dry-run
|
||
```
|
||
|
||
`debug` dodatkowo może przyjąć `--pane TARGET` i `--device PATH`. `deploy`
|
||
przyjmuje `--device PATH` oraz `--backend probe|bootsel`. `probe list --json`
|
||
ma stabilny format maszynowy, a akcje zapisują maszynowy `result.json`.
|
||
|
||
`deploy` jest capability wyłącznie targetów sprzętowych RP2350. W
|
||
`hazard3-sim` artefakt ładuje `run` albo `debug`; osobne `deploy` zwraca
|
||
`unsupported`.
|
||
|
||
## Capabilities zamiast wyjątków w kodzie
|
||
|
||
Launcher nie powinien zawierać warunków typu „jeśli profil rv32i, uruchom ten
|
||
konkretny skrypt”. Profil publikuje capabilities i adaptery:
|
||
|
||
```json
|
||
{
|
||
"profile": "rp2350",
|
||
"targets": ["rp2350-rv", "rp2350-arm"],
|
||
"actions": {
|
||
"build": {"requires": []},
|
||
"test": {"requires": []},
|
||
"run": {"requires": ["matching-deploy-record"]},
|
||
"debug": {"requires": ["probe"]},
|
||
"deploy": {"requires": ["probe"]},
|
||
"shell": {"requires": []}
|
||
},
|
||
"debug_backends": ["openocd"],
|
||
"devices": ["debug-probe", "target-usb-optional"]
|
||
}
|
||
```
|
||
|
||
Karta deklaruje target, a profil dostarcza implementację. Brak capability jest
|
||
normalnym, maszynowo czytelnym wynikiem `unsupported`, nie awarią launchera.
|
||
|
||
## Wspólny interfejs w kontenerze
|
||
|
||
Każdy profil zapewnia kanoniczny dispatcher:
|
||
|
||
```text
|
||
/usr/local/bin/stem-entry
|
||
/usr/local/bin/stem-card
|
||
```
|
||
|
||
Dispatcher przyjmuje `build/test/run/debug/deploy`; manifest może zwrócić
|
||
`unsupported`, jeżeli akcja nie ma sensu dla targetu.
|
||
Skrypty korzystają wyłącznie z manifestu karty i zmiennych kontraktowych:
|
||
|
||
```text
|
||
STEM_WORKSPACE=/workspace
|
||
STEM_PROFILE=hazard3-sim
|
||
STEM_TARGET=hazard3-freertos
|
||
STEM_CARD=inf/bss
|
||
STEM_TASK=task1
|
||
STEM_INSTANCE=hazard3-sim-inf-bss-t1
|
||
STEM_STATE_DIR=/workspace/.stem/instances/<instance>
|
||
STEM_ARTIFACT_DIR=/workspace/.stem/artifacts/<profile>/<target>/<task>
|
||
```
|
||
|
||
Stare `RV_*` są czytane wyłącznie jako fallback w okresie migracji.
|
||
|
||
## Montowania
|
||
|
||
Minimalny zestaw:
|
||
|
||
| Źródło hosta | Cel | Tryb |
|
||
| --- | --- | --- |
|
||
| repo karty | `/workspace` | `rw` |
|
||
| cache profilu | `/cache` | `rw`, osobny named volume |
|
||
| `$XDG_RUNTIME_DIR/stem` | ta sama krótka ścieżka | `rw` |
|
||
| konkretne urządzenie probe | urządzenie | tylko profil `rp2350` |
|
||
|
||
Nie montujemy:
|
||
|
||
- `$HOME/.ssh`;
|
||
- pliku tokenów;
|
||
- całego `/dev`;
|
||
- socketu Docker/Podman;
|
||
- katalogu domowego hosta;
|
||
- repo innych uczniów.
|
||
|
||
Repo jest własnością użytkownika hosta. Rootless Podman uruchamia kontener z
|
||
mapowaniem UID/GID (`keep-id` albo równoważnym), żeby artefakty nie powstawały
|
||
jako root.
|
||
|
||
## Layout tmuxa i Neovima
|
||
|
||
W każdym profilu po `debug` powstaje ten sam układ:
|
||
|
||
```text
|
||
work
|
||
├─ pane 0 (góra): jeden Neovim
|
||
│ ├─ okno lewe: Termdebug/GDB dashboard
|
||
│ └─ okno prawe: edytor źródeł i nvim-dap
|
||
└─ pane 1 (dół): bash w /workspace
|
||
```
|
||
|
||
Lewa i prawa część są oknami Neovima, nie osobnymi pane'ami tmuxa. Hazard3
|
||
trzyma symulator w ukrytym oknie tmuxa `tb`. Prefix tmuxa to `Ctrl-s`. Neovim
|
||
ma identyczne mapowania Termdebug i nvim-dap.
|
||
Opis backendu GDB pochodzi z jednego target descriptor, dzięki czemu oba
|
||
frontendy nie rozjeżdżają się.
|
||
|
||
## Debugger według profilu
|
||
|
||
### `native-amd64`
|
||
|
||
- GDB jest backendem domyślnym;
|
||
- LLDB pozostaje dostępny ręcznie w shellu; wspólny interfejs Termdebug używa GDB;
|
||
- przed debugowaniem powstaje build `-g3 -O0`;
|
||
- `test` uruchamia co najmniej ASan i UBSan.
|
||
|
||
### `hazard3-sim`
|
||
|
||
- obecny codzienny backend: `fast-rsp`, jawnie opisany jako uproszczony;
|
||
- docelowy, planowany backend: `fast-dm`, czyli GDB RSP -> APB/DMI ->
|
||
prawdziwy DM Hazard3;
|
||
- gate zgodności: OpenOCD + JTAG remote-bitbang;
|
||
- planowany simulatorowy BSP FreeRTOS ma używać timera 1 kHz, czyli ticka co
|
||
150 000 cykli przy zegarze gościa 150 MHz; nie jest jeszcze wdrożony.
|
||
|
||
### `rp2350`
|
||
|
||
- OpenOCD/probe dla targetów RISC-V i ARM;
|
||
- picotool dla informacji, resetu i operacji wspieranych przez RP2350;
|
||
- `deploy` zawsze raportuje probe, target, artefakt i jego SHA-256;
|
||
- launcher nie wybiera pierwszego przypadkowego probe, gdy widocznych jest
|
||
kilka urządzeń.
|
||
|
||
Zewnętrzny debug probe i natywne USB płytki są rozdzielone. Standardowy
|
||
debug/flash używa stabilnego probe, który nie re-enumeruje się przy resecie
|
||
targetu. BOOTSEL/picotool jest opcjonalną, krótkotrwałą akcją z tego samego
|
||
profilu: launcher wykrywa bieżący węzeł USB przed każdą fazą i ponownie po
|
||
re-enumeracji. Nie przekazuje całego `/dev`. Build i test offline nie wymagają
|
||
żadnego urządzenia.
|
||
|
||
`run rp2350` wymaga zgodnego rekordu ostatniego deploy dla wybranej płytki i
|
||
artefaktu. Bez niego odmawia wykonania, chyba że użytkownik jawnie zaakceptuje
|
||
istniejący firmware. Zapobiega to testowaniu starego programu.
|
||
|
||
## Źródła projektu
|
||
|
||
Źródła kart i projektów pobiera wyłącznie `stemctl` na hoście, zawsze pod
|
||
`~/dev/workspace` danego konta Unix. Kanoniczny workspace STEM ma postać
|
||
`~/dev/workspace/stem`; nie używamy do pracy katalogów kontenera ani
|
||
przypadkowych klonów poza `~/dev/workspace`. Pierwszy klon launchera trafia do
|
||
`~/dev/workspace/stem/tools/stem-launcher` i od niego zaczyna się cały
|
||
bootstrap workspace. Typowy przepływ to:
|
||
|
||
```bash
|
||
stemctl workspace sync
|
||
stemctl series cards fetch inf pointers
|
||
stemctl debug rp2350 inf pointers 4
|
||
```
|
||
|
||
Katalog karty pozostaje na hoście, na przykład
|
||
`~/dev/workspace/stem/series/inf/pointers/`, i jest przekazywany kontenerowi
|
||
jako bind-mount `/workspace`. Nie klonujemy repozytoriów źródłowych wewnątrz
|
||
kontenera: kontenery są odtwarzalne i mogą zostać usunięte, natomiast host
|
||
zachowuje Git, zmiany ucznia, odpowiedzi i historię pracy. W obrazie lub
|
||
osobnym cache volume mogą znajdować się wyłącznie narzędzia, zależności i
|
||
odtwarzalne cache budowania.
|
||
|
||
Dla Hazard3 po przygotowaniu przez `stemctl env sources SERIES CARD` ten sam
|
||
bind-mount zawiera również rzeczywiste (niebędące symlinkami) katalogi
|
||
`vendor/Hazard3` i `vendor/lab-runtime`. Program, testbench oraz GDB korzystają
|
||
wyłącznie z nich. Build testbencha i ELF trafia do
|
||
`.stem/instances/<instance>/build`, a DWARF zachowuje ścieżki
|
||
`/workspace/vendor/Hazard3/...` i `/workspace/src/...`. `/opt` pozostaje
|
||
miejscem toolchainów i programów obrazu; nie jest źródłem kodu wyświetlanego w
|
||
Neovimie ani GDB.
|
||
|
||
## Instancje i sockety MCP
|
||
|
||
Stabilna nazwa logiczna:
|
||
|
||
```text
|
||
<profile>-<series>-<card>-<task>
|
||
```
|
||
|
||
Aktualny container ID jest pobierany z `podman inspect`. Pełne nazwy logiczne
|
||
są w labels i registry, a sockety używają krótkich kluczy:
|
||
|
||
```text
|
||
$XDG_RUNTIME_DIR/stem/<thread-key>/<instance-key>/<cid12>/t.sock
|
||
$XDG_RUNTIME_DIR/stem/<thread-key>/<instance-key>/<cid12>/n.sock
|
||
$XDG_RUNTIME_DIR/stem/<thread-key>/<instance-key>/current
|
||
```
|
||
|
||
Klucze wątku i instancji mają 12 znaków, `cid12` jest prefiksem bieżącego ID.
|
||
Registry przechowuje pełne wartości. Host i kontener używają tej samej ścieżki,
|
||
a całkowita długość musi być mniejsza lub równa 100 bajtów, pozostawiając
|
||
zapas względem linuksowego limitu `sun_path`.
|
||
|
||
Wrapper MCP otrzymuje `instance`, sprawdza label kontenera, aktualny ID i typ
|
||
socketu. Dopiero wtedy łączy się z tmuxem albo Neovimem. Pozwala to Codexowi
|
||
obsługiwać kilka kontenerów bez przypadkowego wejścia do starej sesji.
|
||
|
||
Publiczne wejścia launchera to `stemctl mcp tmux INSTANCE` oraz
|
||
`stemctl mcp nvim INSTANCE`. Hostowy wrapper wykonuje wyłącznie resolver i
|
||
`podman exec`; sam serwer wraz z przypiętymi zależnościami działa wewnątrz
|
||
zweryfikowanego kontenera. Neovim MCP publikuje również stan i komendy
|
||
Termdebug/nvim-dap.
|
||
|
||
Dla jednego stale działającego klienta hostowego dostępny jest również jawny,
|
||
atomowy przełącznik obu socketów:
|
||
|
||
```bash
|
||
stemctl mcp list
|
||
stemctl mcp select INSTANCE_OR_CONTAINER_ID
|
||
stemctl mcp status
|
||
```
|
||
|
||
`mcp select` sprawdza registry, pełny ID i label kontenera, aktywnie testuje
|
||
serwery Neovima i tmuxa, a następnie jednym `rename(2)` przełącza symlink
|
||
`$XDG_RUNTIME_DIR/stem/mcp-selected/current`. Jeśli choć jeden serwer nie
|
||
odpowiada, poprzedni wybór pozostaje bez zmian.
|
||
|
||
Katalog socketów jest tworzony przez launcher na hoście i bind-mountowany do
|
||
kontenera pod identyczną ścieżką. Dopiero `nvim` i `tmux` działające w
|
||
kontenerze tworzą odpowiednio `n.sock` i `t.sock`; hostowy Codex łączy się z
|
||
tym samym plikiem przez widok hosta. Sockety nie są przekazywane przez sieć ani
|
||
nie dają kontenerowi dostępu do socketu Podmana.
|
||
|
||
Połączenia MCP mają profil `observe`, `assist` albo `full`. `full` jest
|
||
przeznaczony dla nauczyciela: może wykonywać dowolne działania dostępne w
|
||
Neovimie, tmuxie i terminalu wybranego kontenera jako konto ucznia. Profile
|
||
`observe` i `assist` muszą używać oddzielnych, ograniczonych providerów;
|
||
nie wolno udawać ograniczenia przez samą zmienną środowiskową przy providerze
|
||
udostępniającym ogólny Vimscript lub polecenia pane’a.
|
||
|
||
MCP nie jest czwartym kontenerem. Bridge jest częścią `dev-ui-base`, a hostowy
|
||
resolver jest częścią `stem-launcher`.
|
||
|
||
## Cykl życia
|
||
|
||
```bash
|
||
stemctl start hazard3-sim inf bss 1
|
||
stemctl status hazard3-sim --instance hazard3-sim-inf-bss-t1
|
||
stemctl attach hazard3-sim --instance hazard3-sim-inf-bss-t1
|
||
stemctl stop hazard3-sim --instance hazard3-sim-inf-bss-t1
|
||
stemctl rm hazard3-sim --instance hazard3-sim-inf-bss-t1
|
||
```
|
||
|
||
`start` jest idempotentne dla zgodnej instancji. Jeżeli zmienił się lokalny ID
|
||
wyniku builda albo konfiguracja targetu, launcher odtwarza kontener.
|
||
`stop/start` zachowuje container ID; `rm/start` tworzy nowy ID i nowy katalog
|
||
socketów. Stan karty pozostaje na hoście.
|
||
|
||
## Zdalny komputer
|
||
|
||
Na komputer ucznia wchodzimy przez SSH z kluczem i uruchamiamy `stemctl` tam:
|
||
|
||
```bash
|
||
ssh uczen-lab
|
||
cd ~/dev/workspace/stem/tools/stem-launcher
|
||
./stemctl status hazard3-sim --instance hazard3-sim-inf-bss-t1
|
||
./stemctl debug hazard3-sim inf bss 1
|
||
```
|
||
|
||
GDB, UART i MCP nasłuchują na loopback albo Unix socketach. Jeżeli potrzebny
|
||
jest dostęp z komputera nauczyciela, launcher generuje jawne polecenie tunelu
|
||
SSH. Nie otwiera portów na `0.0.0.0`.
|
||
|
||
## Budowanie i cache
|
||
|
||
`dev-ui-base` zawiera przypięte binaria/dependencies UI, lecz nie zawiera
|
||
często zmienianych `configs/` i entrypointów. Trzy toolchainowe stage'e
|
||
dziedziczą z niego, instalują ciężkie SDK/kompilatory, a dopiero finalne stage'e
|
||
kopiują konfigurację UI i skrypty. Kod karty jest montowany, nie kopiowany.
|
||
Zmiana entrypointu, dashboardu albo mapowania klawisza nie może invalidować
|
||
warstwy pobierającej toolchain.
|
||
|
||
## Status implementacji
|
||
|
||
| Element | Status |
|
||
| --- | --- |
|
||
| `native-amd64` | wdrożony i sprawdzony z ASan/UBSan |
|
||
| `hazard3-sim` | wdrożony dla bare metal; `fast-dm` i BSP FreeRTOS są następne |
|
||
| `rp2350` | wdrożony; FreeRTOS 1 kHz buduje ELF/UF2 dla RV i ARM, hardware gate wymaga ACL probe |
|
||
| `stemctl` | wdrożony; `rvctl` jest wrapperem zgodności |
|
||
| pobieranie kart z Git | istnieje w `rvctl` |
|
||
| `build/test/run/debug/deploy` | wdrożony dispatcher manifestu i `result.json` |
|
||
| sockety tmux/nvim | wdrożone z `thread12/instance12/container12` i resolverem |
|
||
| zdalny SSH | kontrakt zaprojektowany, smoke test do dodania |
|