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()
|
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::time_t now = std::time(nullptr);
|
||||||
std::tm localTime{};
|
std::tm localTime{};
|
||||||
static std::mutex timeMutex;
|
#ifdef _WIN32
|
||||||
{
|
localtime_s(&localTime, &now);
|
||||||
std::lock_guard<std::mutex> lock(timeMutex);
|
#else
|
||||||
if (const std::tm* current = std::localtime(&now)) {
|
localtime_r(&now, &localTime);
|
||||||
localTime = *current;
|
#endif
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char buffer[16];
|
char buffer[16];
|
||||||
std::strftime(buffer, sizeof(buffer), "%H:%M:%S", &localTime);
|
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.
|
// model_) and the selection/scroll state are touched only on this (main) thread.
|
||||||
drainModel();
|
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
|
// Main console layout
|
||||||
ImGui::BeginChild("ConsoleContainer", ImVec2(0, 0), false,
|
ImGui::BeginChild("ConsoleContainer", ImVec2(0, 0), false,
|
||||||
ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar);
|
ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar);
|
||||||
@@ -756,11 +761,8 @@ void ConsoleTab::renderOutput()
|
|||||||
output_scroll_y_ = ImGui::GetScrollY();
|
output_scroll_y_ = ImGui::GetScrollY();
|
||||||
scanline_rows_.clear();
|
scanline_rows_.clear();
|
||||||
|
|
||||||
// Build the filtered visible-line index list BEFORE mouse handling (screenToTextPos maps
|
// visible_indices_ / has_text_filter_ / filter_lower_ were already computed once at the top of
|
||||||
// through visible_indices_). Also yields the text-filter state used for highlighting.
|
// render() (before the toolbar). screenToTextPos maps through visible_indices_, so it's ready.
|
||||||
bool has_text_filter = false;
|
|
||||||
std::string filter_lower;
|
|
||||||
computeVisibleLines(has_text_filter, filter_lower);
|
|
||||||
|
|
||||||
// Calculate wrapped heights AND build sub-row segments for each visible line. Each
|
// 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
|
// 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,
|
// Draw the visible lines: accent bars, JSON indent guides, selection + filter highlight,
|
||||||
// scanline capture, and the text itself.
|
// 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::Unindent(padX);
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
@@ -808,7 +810,7 @@ void ConsoleTab::renderOutput()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Filter indicator (text filter only — daemon toggle is already visible in toolbar)
|
// Filter indicator (text filter only — daemon toggle is already visible in toolbar)
|
||||||
if (has_text_filter) {
|
if (has_text_filter_) {
|
||||||
char filterBuf[128];
|
char filterBuf[128];
|
||||||
snprintf(filterBuf, sizeof(filterBuf), TR("console_showing_lines"),
|
snprintf(filterBuf, sizeof(filterBuf), TR("console_showing_lines"),
|
||||||
static_cast<int>(visible_indices_.size()), model_.size());
|
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.
|
// with them so the highlight stays on the text the user selected.
|
||||||
selection_.shiftForEviction(static_cast<int>(dr.popped));
|
selection_.shiftForEviction(static_cast<int>(dr.popped));
|
||||||
|
|
||||||
// Track new output that arrived while the user is scrolled up (for the indicator).
|
// Track new output that arrived while the user is scrolled up (for the "N new lines" indicator).
|
||||||
scroll_.onLinesAdded(static_cast<int>(dr.added));
|
// 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)
|
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
|
std::string context_token_; // hash/address under the cursor at right-click
|
||||||
mutable std::vector<int> visible_indices_; // Cached for selection mapping
|
mutable std::vector<int> visible_indices_; // Cached for selection mapping
|
||||||
bool folding_active_ = true; // fold UI shown only in the unfiltered (foldable) view
|
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),
|
// Wrap layout for the visible lines (segments + per-line heights + cumulative Y),
|
||||||
// recomputed each frame by the pure BuildConsoleLayout (console_text_layout.h) and
|
// recomputed each frame by the pure BuildConsoleLayout (console_text_layout.h) and
|
||||||
|
|||||||
Reference in New Issue
Block a user