Commit Graph
11 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5 4e24c195ac Fix the debugger log stream going out of step after 1024 messages
DebuggerLogListener keeps a ring of the last BUFFER_SIZE messages. read_ and
count_ count messages ever read/written, while messages_ is indexed modulo
BUFFER_SIZE - but the non-overflow path used read_ directly as the index. Once
a session had logged BUFFER_SIZE messages that index was past the end of the
array, so the first copy loop (bounded by BUFFER_SIZE) ran zero times and the
second one handed back messages_[0..readCount-1] instead: real log messages,
just the wrong ones, and every later poll stayed the same distance out of step.

It looks like the tail of the log going missing rather than being wrong, which
is a bad way to find out. A log-only breakpoint in a hot loop reaches 1024
messages in seconds, and then "the last thing logged before the breakpoint hit"
- exactly what such a breakpoint is for - names an event thousands of messages
old. Confirmed against a case with an independently known answer: a log-only
breakpoint recording a register in a loop that runs ~20k times now ends with
the value that register actually held at the final hit, where before it ended
several hundred iterations short of it.

The overflow path was already correct - it starts from nextMessage_, which is
an index - so only the one line changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 18:42:39 -06:00
Henrik RydgårdandClaude Opus 5 9799085c3a Let more than one thing receive the log stream at a time
The log manager had a single external-callback slot, but the WebSocket
debugger registers one per *connection* - LogBroadcaster is a local in the
per-connection handler. So with two clients attached (the bundled JS debugger
in a browser and Tools/wsdbg, say) the second one to connect silently took the
log stream away from the first, and then whichever disconnected first cleared
the slot and stopped delivery to the other as well. A one-shot wsdbg command is
enough to do it: connect, take the stream, exit, and the long-lived listener
that was watching the log goes quiet with nothing to say why.

Make it a list with add/remove by handle. The dispatch loop holds the lock
across the callbacks so a listener can't be freed while one is running - which
is what lets LogBroadcaster delete its listener straight after removing it.
Enabling and disabling LogOutput::ExternalCallback belongs to the list now, and
disabling only happens when the last callback goes away.

libretro registers one of these too, and never removes it; it just moves to the
new call. It can't actually collide with the debugger - the libretro build
doesn't compile Core/Debugger/WebSocket at all - but there's no reason for it
to keep using an API that only has room for one caller.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 15:15:10 -06:00
Henrik Rydgård 14509b7815 Code style updates in SymbolMap.cpp, fix claudism 2026-08-14 13:19:12 +02:00
Henrik RydgårdandClaude Sonnet 5 0c63905095 Debugger: report dropped messages when the log broadcaster's ring buffer overflows
DebuggerLogListener buffers up to 1024 log messages between polls of the
WebSocket event loop (up to 1000Hz under high activity, 60Hz otherwise -
see WebSocket.cpp). A source that logs faster than that - a log-only
breakpoint hit thousands of times in a tight loop is a real example, not
hypothetical, see docs/VSHBootInvestigation.md's Attempt 22/24 - can wrap
the ring buffer before GetMessages() ever reads the oldest entries,
silently losing them. From the client's side this was indistinguishable
from the breakpoint just not firing at all, which cost real debugging time
this session tracking down a red herring before finding the real
mechanism.

GetMessages() already detected the overflow case internally (the
`read_ + BUFFER_SIZE < count_` branch) to avoid returning garbage, but
never reported how many messages were actually lost. Now synthesizes a
warning LogMessage ("N log message(s) dropped - client polling too slow
for this volume") and prepends it to the batch whenever this happens, so
a real gap is visibly distinguishable from "this just never got logged."

Verified via UnitTest.exe all (49/49) and a live PPSSPPHeadless + wsdbg
session confirming normal (non-overflow) log relay still works
end-to-end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-14 11:04:32 +02:00
Henrik Rydgård 96cab3f267 Rework the log manager to be more robust 2024-12-16 12:33:58 +01:00
Henrik Rydgård 3ffff831c7 Refactor LogManager (remove GetInstance()). In headless, add option to log to OutputDebugString on Windows. 2024-12-03 21:39:42 +01:00
Henrik Rydgård 5939270d2c Log: Split out StdioListener from ConsoleListener, making the second Win32-only. 2024-07-21 13:55:40 +02:00
Henrik Rydgård 1025bbcf89 Log level/type: Use enum class instead of the awkward namespace trick.
Just a small cleanup I've wanted to do for a long time.
2023-08-25 11:48:59 +02:00
Unknown W. Brackets 77131e737b Log: Use a separate field for the timestamp.
Better than parsing it since we already have the header separate.  Simpler
too.
2018-06-08 06:59:17 -07:00
Unknown W. Brackets 1f987bf1c1 Debugger: Initial register get/set APIs. 2018-06-08 06:59:16 -07:00
Unknown W. Brackets b37d59e8fa Debugger: Organize WebSocket event handling.
Just a starting point to organize it.  Trying to keep it simple.
2018-06-08 06:59:16 -07:00