Files
dxfmakros/doc/dbg2lsp.md
T
m.stangl d7bcfc3edb [ADD] dbg2lsp.py: AutoLISP-Funktionen automatisch mit ssg_dbg instrumentieren
Neues Entwickler-Tool (bin/dbg2lsp.bat + lib/dbg2lsp.py), das das manuelle
Einfuegen von dbgf/dbg/dbgreturn/dbgopen/dbgclose automatisiert:
- --method NAME: dbgf + dbg je Parameter + dbgreturn um die letzte Rumpf-Form
- --recursive: verfolgt den Aufrufgraphen ueber Dateigrenzen hinweg (Builtins
  werden automatisch uebersprungen, da sie kein defun im Suchpfad haben)
- --add-open DATEI METHODE: dbgopen/dbgclose fuer einen Einstiegspunkt
  (z.B. ein c:BEFEHL-Kommando), inkl. aller (exit)-Stellen
Dokumentiert in doc/dbg2lsp.md, Kurzeintrag in CLAUDE.md.

Ausserdem: set_attributs.py aus doc/tools.md und CLAUDE.md entfernt - das
Skript existiert nicht mehr in lib/.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 15:04:45 +02:00

207 lines
8.9 KiB
Markdown

# dbg2lsp
Instrumentiert AutoLISP-Funktionen (`.lsp`) automatisch mit den `ssg_dbg.lsp`-Routinen
(`dbgf`, `dbg`, `dbgreturn`, `dbgopen`, `dbgclose`) - so wie es beim manuellen Debuggen
bereits von Hand gemacht wird (siehe z.B. `Lisp/KreiselInsert.lsp::ils-eckrad-insert`).
Erspart das muehsame und fehleranfaellige manuelle Einfuegen dieser Aufrufe in
lange Aufrufketten (Kreisel/VarioFoerderer/Gefaellestrecke/Separator-Scanner o.ae.),
bevor ein Testlauf in BricsCAD gestartet wird.
- [Aufruf](#aufruf)
- [Was wird eingefuegt](#was-wird-eingefuegt)
- [--method](#--method)
- [--recursive](#--recursive)
- [--add-open](#--add-open)
- [Schalter](#schalter)
- [Typischer Ablauf](#typischer-ablauf)
- [Beispiele](#beispiele)
- [Funktionsweise / Aufloesung](#funktionsweise--aufloesung)
- [Grenzen](#grenzen)
- [Idempotenz](#idempotenz)
## Aufruf
```
bin\dbg2lsp.bat <datei.lsp> [--method NAME ...] [--recursive] [--add-open DATEI METHODE] [Schalter]
```
Das Batch-Skript ruft `setenv.bat` auf und startet `lib/dbg2lsp.py` mit den
uebergebenen Argumenten. `<datei.lsp>` dient als Anker (ihr Verzeichnis wird immer
mitdurchsucht) - die gesuchte Funktion muss nicht zwingend in genau dieser Datei
stehen, siehe [Funktionsweise](#funktionsweise--aufloesung).
## Was wird eingefuegt
### --method
Fuer die angegebene Funktion (mehrfach angebbar) wird direkt als erste Rumpf-Form
```lisp
(dbgf "funktionsname")
(dbg 'parameter1)
(dbg 'parameter2)
...
```
eingefuegt (ein `dbg`-Aufruf je Eingabeparameter - die Locals nach `/` in der
Parameterliste NICHT, das sind keine Eingabewerte), und die **letzte Form des
Funktionsrumpfs** wird eingeklammert:
```lisp
(dbgreturn <letzte Rumpf-Form>)
```
`dbgreturn` loggt und liefert den Rueckgabewert unveraendert weiter (siehe
`Lisp/ssg_dbg.lsp`) - das Verhalten der Funktion aendert sich dadurch nicht.
### --recursive
Verfolgt zusaetzlich den Aufrufgraphen: jede Funktion, die im (Original-)Rumpf
aufgerufen wird - auch beliebig verschachtelt, z.B. in einem `if`/`progn`, aber
NICHT innerhalb einer `'quotierten Liste` (die ist reine Datenliste, z.B.
`'(("OSMODE") ("CECOLOR"))`) - wird ebenfalls mit `--method`-Logik instrumentiert,
sofern sich ihr `(defun ...)` in einem der durchsuchten Verzeichnisse findet.
AutoLISP-/BricsCAD-Builtins (`princ`, `command`, `strcat`, `rtos`, `entmake`, `vla-*`, ...)
haben dort naturgemaess kein `defun` und werden dadurch automatisch **nicht** angefasst -
es gibt keine Builtin-Blacklist zu pflegen. Die `ssg_dbg.lsp`-Funktionen selbst
(`dbgf`, `dbg`, `dbgp`, `dbgmsg`, `dbgreturn`, `dbgopen`, `dbgclose`, `dbgflush`, `dbgon`, `dbgoff`
und ihre internen `dbg-*`-Helfer) sind fest ausgeschlossen, damit eine bereits
instrumentierte Funktion nicht dazu fuehrt, dass das Debug-Framework sich selbst
instrumentiert.
Bereits instrumentierte Funktionen werden uebersprungen (siehe [Idempotenz](#idempotenz)),
ihr Rumpf wird aber trotzdem nach weiteren, noch nicht instrumentierten Aufrufen
durchsucht - die Rekursion bricht dadurch nicht an einer schon bearbeiteten Stelle ab.
### --add-open
Fuegt in der angegebenen Methode (typischerweise ein `c:BEFEHL`-Kommando, das
einen ganzen Testlauf umspannt) ein:
```lisp
(dbgopen "<dbg-datei>" "<envvar>")
```
als erste Rumpf-Form, sowie
```lisp
(dbgclose)
```
vor **jedem** `(exit)`-Aufruf im Rumpf (auch verschachtelt) und zusaetzlich am
Ende des Rumpfs - ausser die letzte Rumpf-Form ist bereits selbst ein `(exit)`
(dann waere ein zweites `dbgclose` ohnehin unerreichbarer Code).
`--add-open DATEI METHODE` ist unabhaengig von `--method`/`--recursive` nutzbar
und mehrfach angebbar; alle drei Mechanismen lassen sich in einem Aufruf kombinieren.
## Schalter
| Schalter | Beschreibung |
|---|---|
| `datei` (Pflicht, Position 1) | Anker-`.lsp`-Datei; ihr Verzeichnis wird immer mitdurchsucht |
| `--method NAME` | Funktion mit `dbgf`/`dbg`/`dbgreturn` instrumentieren (mehrfach angebbar) |
| `--recursive` | Auch alle im Rumpf aufgerufenen projekteigenen Funktionen instrumentieren |
| `--search-dir DIR` | Weiteres Verzeichnis nach `.lsp`-Dateien durchsuchen (mehrfach angebbar). Default: `Lisp/` und das Verzeichnis von `datei` |
| `--max-depth N` | Maximale Rekursionstiefe fuer `--recursive` (Default: `3`) |
| `--exclude NAME` | Funktionsname von `--recursive` ausschliessen (mehrfach angebbar) |
| `--add-open DATEI METHODE` | `dbgopen`/`dbgclose` in `METHODE` (aus `DATEI`) einfuegen (mehrfach angebbar) |
| `--dbg-file NAME` | Dateiname fuer `(dbgopen ...)`. Default: aus dem `--add-open`-Methodennamen abgeleitet (`c:TEST_MUBEA` -> `test_mubea.dbg`) |
| `--dbg-envvar NAME` | Umgebungsvariable fuer `(dbgopen ...)` (Default: `DXFM_LOG`) |
| `--force` | Auch instrumentieren, wenn bereits `(dbgf ...)`/`(dbgopen ...)` vorhanden zu sein scheint |
| `--dry-run` | Nur ein Diff anzeigen, keine Dateien schreiben |
## Typischer Ablauf
1. **Immer zuerst `--dry-run`** laufen lassen und das Diff pruefen (mehrere Dateien
koennen betroffen sein, siehe `--recursive`).
2. Ohne `--dry-run` erneut aufrufen - die Dateien werden direkt (in-place) geschrieben.
3. In BricsCAD `(load ...)` neu laden bzw. den Testlauf starten.
4. Die `.dbg`-Datei (per `--add-open` geoeffnet, Pfad = `%DXFM_LOG%\<--dbg-file>`)
auswerten.
5. Da die Dateien in-place geschrieben werden und ueber Git versioniert sind: nach
dem Debuggen die Debug-Aufrufe entweder manuell entfernen oder die Datei(en)
per `git checkout -- <datei>` verwerfen (das Werkzeug selbst hat keinen
"--remove"-Schalter).
## Beispiele
Eine einzelne Funktion instrumentieren:
```
bin\dbg2lsp.bat tests\test_mubea.lsp --method mubea:build-separator-one --dry-run
```
Eine Funktion UND alles, was sie aufruft (bis Tiefe 3, Default):
```
bin\dbg2lsp.bat tests\test_mubea.lsp --method mubea:build-kreisel --recursive --dry-run
```
Mehrere Einstiegspunkte in einem Lauf, plus Oeffnen/Schliessen der Debug-Datei
im umgebenden Testbefehl - das entspricht genau der Instrumentierung, die fuer
den urspruenglichen Scanner-Bug in `TEST_MUBEA` von Hand gemacht wurde:
```
bin\dbg2lsp.bat tests\test_mubea.lsp ^
--method mubea:build-kreisel ^
--method mubea:build-vario-one ^
--method mubea:build-gf-one ^
--method mubea:build-separator-one ^
--recursive ^
--add-open tests\test_mubea.lsp c:TEST_MUBEA
```
Bestimmte generische Helfer von der Rekursion ausschliessen (z.B. um `ssg-start`/`ssg-end`
nicht mit anzufassen):
```
bin\dbg2lsp.bat tests\test_mubea.lsp --method mubea:build-kreisel --recursive ^
--exclude ssg-start --exclude ssg-end
```
## Funktionsweise / Aufloesung
- `datei` wird zuerst relativ zum aktuellen Arbeitsverzeichnis, sonst relativ zur
Projektwurzel (erkannt an `.git/` bzw. `Lisp/`, ausgehend von `datei` nach oben
gesucht) aufgeloest.
- Durchsucht werden: das Verzeichnis von `datei`, `Lisp/` (relativ zur Projektwurzel),
jedes `--search-dir` sowie das Verzeichnis jeder `--add-open`-Datei - jeweils
rekursiv nach `*.lsp`.
- Aus all diesen Dateien wird EIN Index `Funktionsname -> (defun ...)` aufgebaut
(case-insensitiv, wie AutoLISP-Symbole zur Laufzeit). `--method`/`--add-open`
muessen daher nicht in `datei` selbst stehen, sondern werden ueberall gefunden.
- Klammer-/String-/Kommentar-Erkennung ist zeichenweise implementiert (kein
vollstaendiger Lisp-Reader) - genuegt aber, um Top-Level-`(defun ...)`-Bloecke,
ihre Parameterliste und die letzte Rumpf-Form zuverlaessig zu erkennen, auch bei
mehrzeiligen Parameterlisten mit Locals nach `/`.
- Alle Aenderungen werden zunaechst als Liste von reinen Text-Einfuegungen
gesammelt (keine Loeschungen) und erst am Ende je Datei angewandt - Positionen
bleiben dadurch unabhaengig von der Bearbeitungsreihenfolge gueltig.
## Grenzen
- **Nur die letzte Rumpf-Form wird zu `dbgreturn`.** Fruehe `(exit)`-Ausstiege
MITTEN im Rumpf werden von `--method` nicht mit einem eigenen `dbgreturn`
versehen (nur `--add-open` behandelt `(exit)`-Stellen explizit, dort aber nur
mit `dbgclose`, nicht mit einem geloggten Rueckgabewert).
- **Kein "--remove".** Rueckbau erfolgt manuell oder per `git checkout -- <datei>`.
- **`--recursive` kann viele Dateien anfassen** - projekteigene Helfer wie
`ssg-start`/`ssg-val`/`ssg-attrib-merge` werden mitinstrumentiert, sobald sie im
Aufrufgraphen auftauchen. `--max-depth` und `--exclude` begrenzen das gezielt;
ohne `--dry-run`-Kontrolle vorher nicht blind auf grosse Funktionen mit hoher
`--max-depth` loslassen.
- **Mehrfache Definitionen desselben Namens** in unterschiedlichen Dateien: es
gewinnt der erste beim Durchsuchen gefundene (Dateireihenfolge nicht garantiert
sortiert-stabil ueber Verzeichnisgrenzen) - in dieser Codebasis kommt das
ueblicherweise nicht vor.
## Idempotenz
Vor dem Einfuegen prueft das Werkzeug, ob die erste Rumpf-Form bereits `(dbgf ...)`
(fuer `--method`) bzw. `(dbgopen ...)` (fuer `--add-open`) ist, und ueberspringt die
Funktion in dem Fall mit einer `[skip]`-Meldung. Mit `--force` wird trotzdem erneut
eingefuegt (fuehrt bei wiederholtem `--force`-Aufruf zu doppelten `dbgf`/`dbg`-Zeilen -
in dem Fall vorher besser die Datei zuruecksetzen).