Skip to main content

debug

Reproduce and diagnose a failing, flaky or hanging silk test or run - CI failures, timeouts, sanitizer reports, crash dumps - and measure test coverage. Use when a test fails, hangs or flakes, when a run wedges, or when the user asks for coverage.

Quellinformationen

Repository
ClickHouse/silk
Letzte Quellaktivität
21. September 2026 um 20:18
Erkannte Sprache von SKILL.md
Englisch
Sterne
332
Forks
14

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
debug
description
Reproduce and diagnose a failing, flaky or hanging silk test or run - CI failures, timeouts, sanitizer reports, crash dumps - and measure test coverage. Use when a test fails, hangs or flakes, when a run wedges, or when the user asks for coverage.
Find the failing job first (the check-ci skill), then reproduce it here from a build matching CI's preset. Read the evidence a run already produced before you run anything else. ## Build the same thing CI ran `./bb -b <debug|release> -s <address|thread|undefined|memory> test -R <regex>`, with `-b` and `-s` as separate flags. Reproduce a timing-dependent failure on `release`: debug timing hides the race. `bb test` and `bb bench` forward unknown flags to ctest, so any ctest option works (`--repeat until-fail:100`, `--output-on-failure`); use the built-in option when `bb` has one (`--timeout`, `--coverage`). `--gtest_filter` never works, because test binaries are never run directly. `-N` lists the matching tests without running them. ## Where the evidence is - The captured test output: `build/<preset>/Testing/Temporary/LastTest.log`. The next run overwrites it, so read it first. - An assertion (`SILK_ASSERT`, `SILK_FAIL`) prints the condition, file, line, message and a libbacktrace-symbolized stack trace into that output. - A hang produces a crash dump. Every test directory sets ctest's `TIMEOUT_SIGNAL_NAME SIGQUIT` with a 60 s grace period, and every test main and perf tool calls `silk::installCrashDumper`. On SIGQUIT or a crash signal a pre-forked dumper attaches gdb (`src/gdb/crash-dumper.py` with `fiber.py`, installed next to the binary) and writes the dump to the process's stderr; the process then cores (crash) or exits 124 (hang). To self-dump a wedged run outside ctest: `timeout --signal=SIGQUIT <seconds> ./bb ...`. ## The gdb scripts `src/gdb/fiber.py` teaches gdb the scheduler's data structures. The build copies it and `crash-dumper.py` next to the binaries, into `build/<preset>/bin/`. Load it in any gdb session on a silk process or core, then use its commands: ``` (gdb) source build/<preset>/bin/fiber.py ``` - `fiber-list [--proxy]` lists every fiber the scheduler knows: the `Fiber *` address, the state, the fiber-main symbol, and for a suspended fiber the suspension site. `--proxy` adds the proxy fibers that stand for non-fiber OS threads. Inspect one with `p *(silk::Fiber *) 0xADDR`. - `fiber-savecontext`, then `fiber-switchcontext <Fiber *>` loads a SUSPENDED or READY fiber's saved registers so that `bt` shows its stack; `fiber-restorecontext` returns to the saved thread. A RUNNING fiber is on an OS thread: use `thread N` instead. - `fiber-dump-scheduler` prints the scheduler-wide state. `fiber-dump-sleep [cpu]` prints the per-processor sleep table and the SleepFutures in each sleepTree, sleepQueue and cancelQueue with deadline, `overdueCycles`, `isSet` and waiter. `fiber-dump-uring [cpu]` prints the SQ/CQ ring counters per processor and decodes any unconsumed CQE. `fiber-dump-counters [cpu]` prints every Perf simple counter, summed across CPUs or for one CPU's slot. - The commands resolve `silk::FiberScheduler` relative to the selected frame's compilation unit. After an attach the selected frame is in libc, so select a frame inside silk first (`thread N`, then `frame M`) when a command reports that it cannot find the scheduler. `crash-dumper.py` is the whole dump as one script: sourcing it runs the thread dump, the fiber dump and the four state dumps in order. Use it to take the dump of a live or wedged process, or of a core, without waiting for a timeout: ``` gdb -batch -p <pid> -ex 'source build/<preset>/bin/crash-dumper.py' gdb -batch <binary> <core> -ex 'source build/<preset>/bin/crash-dumper.py' ``` ## Read a crash dump The dump has four parts, in this order: 1. **OS threads**, grouped by identical stack, one backtrace per group with a count. Idle per-CPU scheduler threads all sit in `ProcessorState::parkThread` / `io_uring_enter2`. The main thread inside `FiberFuture::wait` / `sem_wait` is the proxy fiber's park. 2. **silk fibers** from `fiber-list`, grouped by state, fiber main and suspension site, one backtrace per group taken through `fiber-switchcontext`. 3. **Scheduler state** from `fiber-dump-scheduler`, `fiber-dump-sleep` and `fiber-dump-uring`. A sleepTree entry with positive `overdueCycles` whose future is still unset is a lost sleep wakeup. A parked processor (`sleeping=1`) with `cqReady > 0` or the OVERFLOW flag set is a park wedge: `io_uring_enter2` blocked instead of honouring its timeout, and the decoded CQE names the stuck completion. 4. **Counters** from `fiber-dump-counters`. An imbalanced pair names the failed hand-off: `FiberStarted > FiberStopped` means an injected fiber never finished; `ProxyFiberParked > ProxyFiberWaked` means an external waiter never got its wake. A dump where every queue is empty, every ring shows `cqReady=0, sqInflt=0, sqePend=0` and the awaited fiber is in no list means the fiber was lost between inject and dispatch, or between completion and wake; the counters decide which. ## Repeat until it fails Finish every edit before a loop starts. A build, `bb fmt` or a source edit during a loop relinks the binaries and invalidates the run. One log per stage and per repeat under `build/tmp/`, named before the stage starts; never one shared log that a later stage overwrites. Capture with `tee`, then grep the file: ``` mkdir -p build/tmp for i in $(seq 1 50); do ./bb -b release test --timeout 120 -R '<regex>' 2>&1 | tee build/tmp/flake-$i.log >/dev/null grep -q ' 0 tests failed' build/tmp/flake-$i.log || break done ``` Budget `--timeout` from the test's real passing time plus its internal deadlines. The default is 180 s; a tight ceiling misreads a slow pass as a hang. For a concurrency suite, run TSan yourself and repeat it: `./bb -s thread test -R <suite> --repeat until-fail:100`. One green run proves little, and a subagent's "clean" is a claim, not a result. Run the benchmarks under TSan too, `./bb -s thread bench -R <regex>`; they reach concurrency the unit tests do not. TSan does not see a missing fence: a store-load reordering is an ordering bug, not a data race. Only long stress runs, a written-out interleaving and the disassembly of both halves of the handshake find it. ## Coverage `./bb test --coverage -R '^Suite\.'` builds the `debug-coverage` preset, runs the matching tests and writes `build/debug-coverage/coverage.xml` (Cobertura) plus an HTML report under `build/debug-coverage/html/`. Read the XML: per-file `line-rate` / `branch-rate`, per-line `hits`, per-branch `condition-coverage`. Never hand-parse `coverage.lcov`. Each run overwrites the XML, so copy it to `build/tmp/` before measuring the next component.
Auf GitHub ansehen