fix(console): mop up audit minors — timestamp, filter count, new-line count
- D2: rpcTraceTimestamp used std::localtime (process-wide static tm) guarded by a private mutex that can't stop another thread's localtime from clobbering the shared buffer. Use localtime_r / localtime_s into a local tm (the codebase pattern) and drop the now-useless mutex. Runs on RPC worker threads. - C1: the toolbar's "<N> matches" label read filter_match_count_ before renderOutput recomputed it, so it lagged one frame. Compute the visible/ filtered set once at the top of render() (before the toolbar) and reuse it in renderOutput, so the count and the output share one consistent computation. - C4: the "N new lines" indicator counted the raw drain count, inflating it with lines the active filter hides. Count only newly-added lines that pass the current filter, so the badge matches what the user sees on jumping to bottom. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -59,15 +59,16 @@ ConsoleTab* s_rpc_trace_console = nullptr;
|
||||
|
||||
std::string rpcTraceTimestamp()
|
||||
{
|
||||
// Called on RPC worker threads. std::localtime shares a process-wide static tm, so a private
|
||||
// mutex here can't stop another thread's localtime call from clobbering it between the call and
|
||||
// the copy. Use the reentrant variant into a local tm instead (matches the codebase pattern).
|
||||
std::time_t now = std::time(nullptr);
|
||||
std::tm localTime{};
|
||||
static std::mutex timeMutex;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(timeMutex);
|
||||
if (const std::tm* current = std::localtime(&now)) {
|
||||
localTime = *current;
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
localtime_s(&localTime, &now);
|
||||
#else
|
||||
localtime_r(&now, &localTime);
|
||||
#endif
|
||||
|
||||
char buffer[16];
|
||||
std::strftime(buffer, sizeof(buffer), "%H:%M:%S", &localTime);
|
||||
@@ -297,6 +298,10 @@ void ConsoleTab::render(ConsoleCommandExecutor& exec)
|
||||
// model_) and the selection/scroll state are touched only on this (main) thread.
|
||||
drainModel();
|
||||
|
||||
// Compute the filtered visible-line set once per frame, BEFORE the toolbar — so its "<N> matches"
|
||||
// label reflects the current frame (not last frame's stale count). renderOutput reuses the result.
|
||||
computeVisibleLines(has_text_filter_, filter_lower_);
|
||||
|
||||
// Main console layout
|
||||
ImGui::BeginChild("ConsoleContainer", ImVec2(0, 0), false,
|
||||
ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar);
|
||||
@@ -756,11 +761,8 @@ void ConsoleTab::renderOutput()
|
||||
output_scroll_y_ = ImGui::GetScrollY();
|
||||
scanline_rows_.clear();
|
||||
|
||||
// Build the filtered visible-line index list BEFORE mouse handling (screenToTextPos maps
|
||||
// through visible_indices_). Also yields the text-filter state used for highlighting.
|
||||
bool has_text_filter = false;
|
||||
std::string filter_lower;
|
||||
computeVisibleLines(has_text_filter, filter_lower);
|
||||
// visible_indices_ / has_text_filter_ / filter_lower_ were already computed once at the top of
|
||||
// render() (before the toolbar). screenToTextPos maps through visible_indices_, so it's ready.
|
||||
|
||||
// Calculate wrapped heights AND build sub-row segments for each visible line. Each
|
||||
// segment records which bytes of the source text appear on that visual row, so
|
||||
@@ -785,7 +787,7 @@ void ConsoleTab::renderOutput()
|
||||
|
||||
// Draw the visible lines: accent bars, JSON indent guides, selection + filter highlight,
|
||||
// scanline capture, and the text itself.
|
||||
drawVisibleLines(padX, line_height, has_text_filter, filter_lower);
|
||||
drawVisibleLines(padX, line_height, has_text_filter_, filter_lower_);
|
||||
|
||||
ImGui::Unindent(padX);
|
||||
ImGui::PopStyleVar();
|
||||
@@ -808,7 +810,7 @@ void ConsoleTab::renderOutput()
|
||||
}
|
||||
|
||||
// Filter indicator (text filter only — daemon toggle is already visible in toolbar)
|
||||
if (has_text_filter) {
|
||||
if (has_text_filter_) {
|
||||
char filterBuf[128];
|
||||
snprintf(filterBuf, sizeof(filterBuf), TR("console_showing_lines"),
|
||||
static_cast<int>(visible_indices_.size()), model_.size());
|
||||
@@ -1685,8 +1687,17 @@ void ConsoleTab::drainModel()
|
||||
// with them so the highlight stays on the text the user selected.
|
||||
selection_.shiftForEviction(static_cast<int>(dr.popped));
|
||||
|
||||
// Track new output that arrived while the user is scrolled up (for the indicator).
|
||||
scroll_.onLinesAdded(static_cast<int>(dr.added));
|
||||
// Track new output that arrived while the user is scrolled up (for the "N new lines" indicator).
|
||||
// Count only the newly-added lines that pass the active filter, so the indicator isn't inflated
|
||||
// by lines the current filter / errors-only view hides — the user wouldn't see those on jumping
|
||||
// to the bottom. (The new lines are the last dr.added entries; eviction is from the front.)
|
||||
ConsoleOutputFilter f{filter_text_, s_daemon_messages_enabled, s_errors_only_enabled,
|
||||
s_rpc_trace_enabled, s_app_messages_enabled};
|
||||
const int n = static_cast<int>(model_.size());
|
||||
int visibleAdded = 0;
|
||||
for (int i = std::max(0, n - static_cast<int>(dr.added)); i < n; i++)
|
||||
if (consoleLinePassesFilter(model_[i].text, model_[i].channel, f)) ++visibleAdded;
|
||||
scroll_.onLinesAdded(visibleAdded);
|
||||
}
|
||||
|
||||
void ConsoleTab::addRpcTraceLine(const std::string& source, const std::string& method)
|
||||
|
||||
@@ -175,6 +175,8 @@ private:
|
||||
std::string context_token_; // hash/address under the cursor at right-click
|
||||
mutable std::vector<int> visible_indices_; // Cached for selection mapping
|
||||
bool folding_active_ = true; // fold UI shown only in the unfiltered (foldable) view
|
||||
bool has_text_filter_ = false; // computed once per frame (before the toolbar draws it)
|
||||
std::string filter_lower_; // lowercased filter needle for match highlighting
|
||||
|
||||
// Wrap layout for the visible lines (segments + per-line heights + cumulative Y),
|
||||
// recomputed each frame by the pure BuildConsoleLayout (console_text_layout.h) and
|
||||
|
||||
Reference in New Issue
Block a user