re: withdraw the 'unrestorable rbx' claim — the misses are WaitMultiple
The 8 threads whose [rbx] did not resolve to a vtable were never in XObject::Wait. The backtrace grep matched WaitMultiple as a substring, and there %rbx is the XObject** array (mov %rsi,%rbx) with the count in %ebp, so [rbx] is objects[0] -- an object pointer, needing a second deref -- not a vtable. The unwind restored rbx correctly for all 18. freeze_waitobj.sh now takes the function and frame index from the backtrace and applies the matching read, and captures twice in one run (healthy and after the ~270s black-screen) so the comparison is within-run. waitobj_report.py tabulates both and diffs them, discarding any value info symbol cannot resolve.
This commit is contained in:
@@ -866,3 +866,50 @@ the preceding gdb session — the gdb wrapper leaves a differently-parented proc
|
||||
tree and the usual cleanup did not catch it. **So the map classification and the
|
||||
multi-word object dump are still unrun**, and the frozen-state capture still has
|
||||
not been taken.
|
||||
|
||||
## ✅ 2026-08-25 — WITHDRAWN: the 8 "unrestorable rbx" readings are a second function
|
||||
|
||||
The entry above is wrong where it matters, and the disassembly says so plainly.
|
||||
`rbx` was restored perfectly for all 18 threads. **Eight of them are not in
|
||||
`XObject::Wait` at all — they are in `XObject::WaitMultiple`**, and `%rbx` does
|
||||
not mean the same thing in the two functions. The grep that produced the table
|
||||
matched `XObject::Wait` as a *substring*, so `WaitMultiple` frames were pooled in
|
||||
with `Wait` frames and read with the wrong rule.
|
||||
|
||||
Reading the two prologues settles it (baseline `c1b57f93b`, no run needed):
|
||||
|
||||
```
|
||||
8fbc90 XObject::Wait(this, ...) 8fbfc0 XObject::WaitMultiple(count, objects, ...)
|
||||
push %rbp/%r15/%r14/%rbx/%rax push %rbp/%r15/%r14/%r13/%r12/%rbx
|
||||
mov %r8,%r15 sub $0x218,%rsp
|
||||
mov %ecx,%ebp mov %rsi,%rbx <-- rbx = XObject** objects
|
||||
mov %rdi,%rbx <-- rbx = this mov %edi,%ebp <-- ebp = count
|
||||
```
|
||||
|
||||
and the loop just below `WaitMultiple`'s prologue confirms what `rbx` points at
|
||||
by using it:
|
||||
|
||||
```
|
||||
8fbff0: mov (%rbx,%r15,8),%rdi ; rdi = objects[i]
|
||||
8fbff4: mov (%rdi),%rax ; rax = objects[i]->vtable
|
||||
8fbff7: call *0x20(%rax) ; a virtual call on it
|
||||
```
|
||||
|
||||
So for a `WaitMultiple` frame `[rbx]` is `objects[0]` — **an `XObject*`, not a
|
||||
vtable** — which is exactly the shape of the eight "misses": pointers into the
|
||||
mmap region where the kernel objects live. They needed **two** derefs, not one.
|
||||
The correct read is `[[rbx + 8i]]` for `i < ebp`.
|
||||
|
||||
Predicted values from the healthy capture, to be checked against the next run:
|
||||
`objects[0]` was `0x7ffd08bcdeb0` for threads 79 and 80 and `0x7ffcfc0911f0`
|
||||
for thread 78 — two threads sharing one object is itself consistent with a
|
||||
group of workers waiting on the same handle.
|
||||
|
||||
**What survives from the previous entry:** the two confirmed object types
|
||||
(`XEvent` ×8, `XSemaphore` ×2) and the self-validating read — a value counts only
|
||||
if it resolves to a `vtable for …` symbol. **What is withdrawn:** "`rbx` could
|
||||
not be restored for eight threads", and with it the claim that the reading is
|
||||
only 10/18 reliable. The unwind was never the problem; the parser was.
|
||||
|
||||
`freeze_waitobj.sh` now parses the function name and frame index out of the
|
||||
backtrace instead of assuming frame 3, and applies the matching rule to each.
|
||||
|
||||
Reference in New Issue
Block a user