diff --git a/.trace_ignore b/.trace_ignore new file mode 100644 index 0000000..73e500a --- /dev/null +++ b/.trace_ignore @@ -0,0 +1,20 @@ +# Functions to exclude from automatic TRACE_ insertion. +# One per line. Supports bare names (main) or qualified (Class::method). +# Blank lines and #comments are ignored. + +# SAX parser callbacks — called per JSON token, far too hot to trace +SaxHandler::key +SaxHandler::handle_number +SaxHandler::string +SaxHandler::args_append +SaxHandler::start_object +SaxHandler::end_object +SaxHandler::escape_json_string +SaxHandler::finish_event + +# Flame graph tree building — called per event during indexing +FlameGraphPanel::find_or_create_child +FlameGraphPanel::find_or_create_root + +# Called multiple times per frame for every action +KeyBindings::is_pressed diff --git a/scripts/find_noisy_traces.py b/scripts/find_noisy_traces.py new file mode 100755 index 0000000..9b37eff --- /dev/null +++ b/scripts/find_noisy_traces.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +"""Find functions called more than a given rate in a Chrome trace JSON file. + +Usage: + python3 scripts/find_noisy_traces.py [--threshold 200] + +Streams the file so it can handle multi-GB traces without loading into memory. +""" + +import argparse +import ijson +from collections import Counter + + +def main(): + p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + p.add_argument("trace", help="Path to Chrome trace JSON file") + p.add_argument("--threshold", type=float, default=200, help="Min calls/second to report (default: 200)") + args = p.parse_args() + + counts = Counter() + min_ts = float("inf") + max_ts = 0 + + with open(args.trace, "rb") as f: + for event in ijson.items(f, "traceEvents.item"): + name = event.get("name", "") + ts = event.get("ts", 0) + if ts: + min_ts = min(min_ts, ts) + max_ts = max(max_ts, ts) + if name: + counts[name] += 1 + + duration_s = (max_ts - min_ts) / 1_000_000 + total = sum(counts.values()) + min_count = args.threshold * duration_s + + print(f"Trace duration: {duration_s:.1f}s") + print(f"Total events: {total}") + print(f"Threshold: {args.threshold:.0f}/s (>{int(min_count)} total)") + print() + + found = 0 + for name, count in counts.most_common(): + rate = count / duration_s + if rate < args.threshold: + break + found += 1 + print(f" {rate:8.0f}/s {count:8d}x {name}") + + if not found: + print(" (none)") + + +if __name__ == "__main__": + main() diff --git a/scripts/insert_trace.py b/scripts/insert_trace.py new file mode 100755 index 0000000..b3437a4 --- /dev/null +++ b/scripts/insert_trace.py @@ -0,0 +1,484 @@ +#!/usr/bin/env python3 +"""Insert a tracing macro into every C++ function body using libclang. + +Usage: + python3 scripts/insert_trace.py [options] + +Options: + --build-dir DIR Path to build dir with compile_commands.json (default: build) + --macro MACRO Macro to insert (default: TRACE_FUNCTION()) + --include HEADER Header to ensure is included (default: tracing.h) + --commit Actually modify files (default is dry-run) + --remove Strip existing TRACE_ macros first, then insert standardized ones + --skip-pattern PAT Regex pattern for function names to skip (can repeat) + --ignore-file FILE File with function names to skip, one per line (default: .trace_ignore) + --only GLOB Only process files matching this glob (e.g. "src/ui/*.cpp") + --min-lines N Skip functions with fewer than N lines (default: 1) + +The macro is inserted as the first statement after the opening brace of each +function definition. Functions that already contain any TRACE_ macro call +are skipped. +""" + +import argparse +import json +import os +import re +import sys +from pathlib import Path +from fnmatch import fnmatch + +import clang.cindex as ci + + +def parse_args(): + p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + p.add_argument("--build-dir", default="build", help="Build directory with compile_commands.json") + p.add_argument("--macro", default='TRACE_FUNCTION_CAT("{cat}")', + help='Macro call to insert. {cat} is replaced with the directory-based category ' + '(default: TRACE_FUNCTION_CAT("{cat}")). Use TRACE_FUNCTION() for no category.') + p.add_argument("--include", default="tracing.h", help="Header to ensure is included") + p.add_argument("--commit", action="store_true", help="Actually modify files (default is dry-run)") + p.add_argument("--remove", action="store_true", help="Strip existing TRACE_ macros first, then insert standardized ones") + p.add_argument("--skip-pattern", action="append", default=[], help="Regex for function names to skip") + p.add_argument("--ignore-file", default=".trace_ignore", + help="File with function names to skip, one per line (default: .trace_ignore)") + p.add_argument("--only", default=None, help="Only process files matching this glob") + p.add_argument("--min-lines", type=int, default=1, help="Skip functions shorter than N lines") + return p.parse_args() + + +def load_ignore_list(ignore_file): + """Load function names from an ignore file, one per line. Blank lines and #comments skipped.""" + if not os.path.exists(ignore_file): + return set() + names = set() + with open(ignore_file) as f: + for line in f: + line = line.strip() + if line and not line.startswith("#"): + names.add(line) + return names + + +def category_for_file(filepath, project_root): + """Derive a tracing category from the file's directory relative to src/. + + src/ui/foo.cpp -> "ui" + src/model/bar.cpp -> "model" + src/parser/baz.cpp -> "parser" + src/platform/x.cpp -> "platform" + src/app.cpp -> "app" + tests/test_foo.cpp -> "tests" + """ + rel = os.path.relpath(filepath, project_root) + parts = Path(rel).parts # e.g. ("src", "ui", "foo.cpp") + if len(parts) >= 3 and parts[0] == "src": + return parts[1] # subdirectory name: ui, model, parser, platform + elif len(parts) >= 2 and parts[0] == "src": + return "app" # top-level src/ files + elif parts[0] == "tests": + return "tests" + else: + return "other" + + +def load_compile_commands(build_dir): + path = os.path.join(build_dir, "compile_commands.json") + if not os.path.exists(path): + print(f"Error: {path} not found. Run cmake -B {build_dir} first.", file=sys.stderr) + sys.exit(1) + with open(path) as f: + return json.load(f) + + +def get_project_files(commands, project_root, only_glob): + """Filter compile_commands to project source files only.""" + files = [] + for entry in commands: + filepath = entry["file"] + # Skip files outside project root (deps, build artifacts) + if not filepath.startswith(project_root): + continue + # Only .cpp files (skip headers parsed via compile_commands) + if not filepath.endswith(".cpp"): + continue + # Skip test files and third-party + rel = os.path.relpath(filepath, project_root) + if rel.startswith("build/") or rel.startswith("third_party/") or rel.startswith("external/"): + continue + if rel.startswith("tests/"): + continue + if only_glob and not fnmatch(rel, only_glob): + continue + files.append((filepath, entry)) + # Deduplicate by filepath (compile_commands can have duplicates) + seen = set() + deduped = [] + for filepath, entry in files: + if filepath not in seen: + seen.add(filepath) + deduped.append((filepath, entry)) + return deduped + + +def extract_compile_args(entry): + """Parse compiler args from a compile_commands entry, keeping includes and defines.""" + cmd = entry.get("command", "") or " ".join(entry.get("arguments", [])) + tokens = cmd.split() + args = [] + skip_next = False + for i, tok in enumerate(tokens): + if skip_next: + skip_next = False + continue + if tok.startswith("-I") or tok.startswith("-D") or tok.startswith("-std="): + args.append(tok) + elif tok in ("-I", "-D", "-isystem"): + if i + 1 < len(tokens): + args.append(tok) + args.append(tokens[i + 1]) + skip_next = True + elif tok.startswith("-isystem"): + args.append(tok) + # Add common clang flags for C++ parsing + if not any(t.startswith("-std=") for t in args): + args.append("-std=c++17") + return args + + +def find_function_body_brace(source_bytes, cursor): + """Find the byte offset of the opening '{' of a function body. + + libclang's cursor extent for a function definition includes the entire + function (signature + body). We need to find the '{' that starts the + compound statement body. + """ + # Look for the compound statement child + for child in cursor.get_children(): + if child.kind == ci.CursorKind.COMPOUND_STMT: + # The compound statement starts at the '{' + offset = child.extent.start.offset + # Verify it's actually a brace + if offset < len(source_bytes) and source_bytes[offset:offset + 1] == b'{': + return offset + return None + + +def function_already_traced(source_bytes, body_start, body_end): + """Check if the function body already contains a TRACE_ macro.""" + body = source_bytes[body_start:body_end].decode("utf-8", errors="replace") + return bool(re.search(r'\bTRACE_\w+\s*\(', body)) + + +def count_body_lines(source_bytes, body_start, body_end): + """Count lines in the function body (excluding braces).""" + body = source_bytes[body_start:body_end] + return body.count(b'\n') + + +def count_body_statements(compound_cursor): + """Count direct child statements/expressions in a compound statement.""" + return sum(1 for _ in compound_cursor.get_children()) + + +def find_insertion_point(source_bytes, brace_offset): + """Find the byte offset where the macro should be inserted. + + Returns the offset right after the opening '{' and any trailing whitespace + on the same line, so we insert on the next line. + """ + pos = brace_offset + 1 + # Skip to end of the line containing '{' + while pos < len(source_bytes) and source_bytes[pos:pos + 1] in (b' ', b'\t'): + pos += 1 + if pos < len(source_bytes) and source_bytes[pos:pos + 1] == b'\n': + pos += 1 + return pos + + +def detect_indent(source_bytes, brace_offset): + """Detect the indentation of the line containing the opening brace, + then add one level (4 spaces).""" + # Walk back to find start of line + line_start = brace_offset + while line_start > 0 and source_bytes[line_start - 1:line_start] != b'\n': + line_start -= 1 + # Extract leading whitespace + indent = b"" + pos = line_start + while pos < brace_offset and source_bytes[pos:pos + 1] in (b' ', b'\t'): + indent += source_bytes[pos:pos + 1] + pos += 1 + # Add one indent level + return indent + b" " + + +def ensure_include(source_text, header): + """If the file doesn't include the header, add it after the last existing #include.""" + include_directive = f'#include "{header}"' + if include_directive in source_text: + return source_text, False + + lines = source_text.split('\n') + last_include_idx = -1 + for i, line in enumerate(lines): + if line.strip().startswith('#include'): + last_include_idx = i + + if last_include_idx >= 0: + lines.insert(last_include_idx + 1, include_directive) + else: + # No includes at all — put at top + lines.insert(0, include_directive) + + return '\n'.join(lines), True + + +def process_file(filepath, entry, args, index, project_root, source_override=None, + ignore_names=None): + """Process a single source file, returning list of insertions. + + If source_override is provided (as a string), it is used instead of reading + from disk. libclang also receives it via unsaved_files so the AST matches. + """ + if ignore_names is None: + ignore_names = set() + rel = os.path.relpath(filepath, project_root) + compile_args = extract_compile_args(entry) + + unsaved = [] + if source_override is not None: + unsaved = [(filepath, source_override)] + + tu = index.parse(filepath, args=compile_args, + unsaved_files=unsaved, + options=ci.TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD) + + if not tu: + print(f" Warning: failed to parse {filepath}", file=sys.stderr) + return [] + + if source_override is not None: + source_bytes = source_override.encode("utf-8") + else: + with open(filepath, "rb") as f: + source_bytes = f.read() + + insertions = [] # list of (offset, macro_text_bytes) + skip_patterns = [re.compile(p) for p in args.skip_pattern] + + def visit(cursor): + # Only look at function/method definitions in this file + if cursor.location.file and cursor.location.file.name != filepath: + return + + is_func_def = ( + cursor.kind in (ci.CursorKind.FUNCTION_DECL, ci.CursorKind.CXX_METHOD, + ci.CursorKind.CONSTRUCTOR, ci.CursorKind.DESTRUCTOR, + ci.CursorKind.FUNCTION_TEMPLATE) + and cursor.is_definition() + ) + + if is_func_def: + short_name = cursor.spelling or cursor.displayname + # Build qualified name (e.g. Namespace::Class::method) + parts = [] + parent = cursor.semantic_parent + while parent and parent.kind != ci.CursorKind.TRANSLATION_UNIT: + if parent.spelling: + parts.append(parent.spelling) + parent = parent.semantic_parent + parts.reverse() + parts.append(short_name) + qualified_name = "::".join(parts) + name = qualified_name + + # Skip ignored functions (match against qualified or bare name) + if name in ignore_names or short_name in ignore_names: + print(f" skip {rel}:{name} (in ignore list)") + return + + # Skip patterns + for pat in skip_patterns: + if pat.search(name): + print(f" skip {rel}:{name} (matches pattern '{pat.pattern}')") + return + + brace_offset = find_function_body_brace(source_bytes, cursor) + if brace_offset is None: + print(f" skip {rel}:{name} (no body brace found)") + return + + # Get body extent + for child in cursor.get_children(): + if child.kind == ci.CursorKind.COMPOUND_STMT: + body_end = child.extent.end.offset + break + else: + print(f" skip {rel}:{name} (no compound statement)") + return + + # Skip already-traced + if function_already_traced(source_bytes, brace_offset, body_end): + print(f" skip {rel}:{name} (already traced)") + return + + # Skip short functions + if count_body_lines(source_bytes, brace_offset, body_end) < args.min_lines: + print(f" skip {rel}:{name} (fewer than {args.min_lines} lines)") + return + + # Skip single-statement functions (e.g. getters, simple returns) + if count_body_statements(child) <= 1: + print(f" skip {rel}:{name} (single statement)") + return + + # Compute insertion + insert_at = find_insertion_point(source_bytes, brace_offset) + indent = detect_indent(source_bytes, brace_offset) + cat = category_for_file(filepath, project_root) + macro_text = args.macro.replace("{cat}", cat) + macro_line = indent + macro_text.encode("utf-8") + b";\n" + insertions.append((insert_at, macro_line, name)) + + for child in cursor.get_children(): + visit(child) + + visit(tu.cursor) + return insertions + + +def apply_insertions(filepath, insertions, include_header, dry_run, source_override=None): + """Apply all insertions to a file (in reverse offset order to preserve positions).""" + if source_override is not None: + source_text = source_override + else: + with open(filepath, "r") as f: + source_text = f.read() + + # Ensure include is present + source_text, include_added = ensure_include(source_text, include_header) + + source_bytes = source_text.encode("utf-8") + + # Sort by offset descending so earlier inserts don't shift later offsets + # If include was added, we need to adjust offsets + if include_added: + # Recalculate: the include adds bytes. But since insertions were computed + # on the original file, we need to adjust. The include is added near the + # top, so all offsets shift by the include line length. + include_line = f'#include "{include_header}"\n' + shift = len(include_line.encode("utf-8")) + insertions = [(off + shift, data, name) for off, data, name in insertions] + + insertions.sort(key=lambda x: x[0], reverse=True) + + for offset, macro_bytes, name in insertions: + source_bytes = source_bytes[:offset] + macro_bytes + source_bytes[offset:] + + if not dry_run: + with open(filepath, "wb") as f: + f.write(source_bytes) + + return include_added + + +def remove_traces(filepath): + """Remove all TRACE_ macro lines from a file. + + Returns (cleaned_text, removed) where cleaned_text is the file content + with traces stripped and removed is a list of (lineno, text) tuples. + Does NOT write to disk — caller decides whether to persist. + """ + with open(filepath, "r") as f: + lines = f.readlines() + + trace_pattern = re.compile(r'^\s*TRACE_\w+\s*\(.*\)\s*;\s*$') + new_lines = [] + removed = [] + for i, line in enumerate(lines): + if trace_pattern.match(line): + removed.append((i + 1, line.rstrip())) + else: + new_lines.append(line) + + return "".join(new_lines), removed + + +def main(): + args = parse_args() + project_root = os.path.abspath(".") + build_dir = os.path.join(project_root, args.build_dir) + ignore_names = load_ignore_list(os.path.join(project_root, args.ignore_file)) + + commands = load_compile_commands(build_dir) + files = get_project_files(commands, project_root, args.only) + + if not files: + print("No project source files found in compile_commands.json") + sys.exit(1) + + print(f"Processing {len(files)} source files...") + print(f"Macro: {args.macro}") + print(f"Include: {args.include}") + if ignore_names: + print(f"Ignore list: {len(ignore_names)} function(s) from {args.ignore_file}") + if args.remove: + print("Pre-pass: removing existing TRACE_ macros before inserting") + if not args.commit: + print("(dry run — no files will be modified, pass --commit to apply)") + print() + + # Pre-pass: strip existing TRACE_ lines, keep cleaned content in memory + cleaned_sources = {} # filepath -> cleaned text (only for --remove) + if args.remove: + total_removals = 0 + for filepath, entry in sorted(files, key=lambda x: x[0]): + rel = os.path.relpath(filepath, project_root) + cleaned_text, removed = remove_traces(filepath) + + if removed: + total_removals += len(removed) + cleaned_sources[filepath] = cleaned_text + print(f" {rel}: {len(removed)} removal(s)") + for lineno, text in removed: + print(f" - line {lineno}: {text}") + if args.commit: + with open(filepath, "w") as f: + f.write(cleaned_text) + + print(f"\nRemoved {total_removals} existing trace(s)") + print() + + index = ci.Index.create() + total_insertions = 0 + total_files_modified = 0 + + for filepath, entry in sorted(files, key=lambda x: x[0]): + rel = os.path.relpath(filepath, project_root) + override = cleaned_sources.get(filepath) + insertions = process_file(filepath, entry, args, index, project_root, + source_override=override, + ignore_names=ignore_names) + + if insertions: + total_files_modified += 1 + total_insertions += len(insertions) + print(f" {rel}: {len(insertions)} insertion(s)") + for _, macro_bytes, name in sorted(insertions, key=lambda x: x[0]): + macro_text = macro_bytes.decode("utf-8").strip() + print(f" + {name}: {macro_text}") + apply_insertions(filepath, insertions, args.include, not args.commit, + source_override=override) + else: + print(f" {rel}: (no changes)") + + print() + print(f"Done: {total_insertions} insertions across {total_files_modified} files") + if not args.commit: + print("(dry run — no files were modified)") + + +if __name__ == "__main__": + main() diff --git a/src/model/query_db.cpp b/src/model/query_db.cpp index f1c0b4a..6ec5bac 100644 --- a/src/model/query_db.cpp +++ b/src/model/query_db.cpp @@ -7,6 +7,7 @@ QueryDb::QueryDb() { } QueryDb::~QueryDb() { + TRACE_FUNCTION_CAT("model"); cancel_query(); if (query_thread_.joinable()) query_thread_.join(); if (index_thread_.joinable()) index_thread_.join(); @@ -14,7 +15,7 @@ QueryDb::~QueryDb() { } void QueryDb::load(const TraceModel& model, std::function on_progress) { - TRACE_SCOPE_CAT("QueryDb::load", "model"); + TRACE_FUNCTION_CAT("model"); if (!db_) return; // Cancel any running query and wait for background indexing @@ -78,7 +79,6 @@ void QueryDb::load(const TraceModel& model, std::function on_progre // Events { - TRACE_SCOPE_CAT("InsertEvents", "model"); sqlite3_stmt* stmt = nullptr; sqlite3_prepare_v2(db_, "INSERT INTO events VALUES (?,?,?,?,?,?,?,?,?,?)", -1, &stmt, nullptr); @@ -115,7 +115,6 @@ void QueryDb::load(const TraceModel& model, std::function on_progre // Processes and threads { - TRACE_SCOPE_CAT("InsertProcessesAndThreads", "model"); sqlite3_stmt* stmt = nullptr; sqlite3_prepare_v2(db_, "INSERT INTO processes VALUES (?,?)", -1, &stmt, nullptr); @@ -143,7 +142,6 @@ void QueryDb::load(const TraceModel& model, std::function on_progre // Counters { - TRACE_SCOPE_CAT("InsertCounters", "model"); sqlite3_stmt* stmt = nullptr; sqlite3_prepare_v2(db_, "INSERT INTO counters VALUES (?,?,?,?)", -1, &stmt, nullptr); @@ -168,13 +166,13 @@ void QueryDb::load(const TraceModel& model, std::function on_progre } void QueryDb::create_indexes_async() { + TRACE_FUNCTION_CAT("model"); if (index_thread_.joinable()) index_thread_.join(); indexing_ = true; indexing_progress_ = 0.0f; index_thread_ = std::thread([this]() { - TRACE_SCOPE_CAT("CreateIndexes", "model"); sqlite3_exec(db_, "CREATE INDEX IF NOT EXISTS idx_events_name ON events(name)", nullptr, nullptr, nullptr); indexing_progress_.store(0.25f, std::memory_order_relaxed); sqlite3_exec(db_, "CREATE INDEX IF NOT EXISTS idx_events_ts ON events(ts)", nullptr, nullptr, nullptr); diff --git a/src/model/trace_model.cpp b/src/model/trace_model.cpp index 16702d6..0b9c1af 100644 --- a/src/model/trace_model.cpp +++ b/src/model/trace_model.cpp @@ -4,7 +4,7 @@ #include void TraceModel::build_index(std::function on_progress) { - TRACE_SCOPE_CAT("BuildIndex", "model"); + TRACE_FUNCTION_CAT("model"); size_t total_threads = 0; for (const auto& proc : processes_) { @@ -20,7 +20,6 @@ void TraceModel::build_index(std::function on_progress) { // Uses key extraction for cache-friendly sorting: copy (ts, dur, index) into a // contiguous array, sort that, then write sorted indices back. { - TRACE_SCOPE_CAT("SortEvents", "model"); struct SortKey { double ts; double dur; @@ -51,7 +50,6 @@ void TraceModel::build_index(std::function on_progress) { // Match B/E pairs and compute duration { - TRACE_SCOPE_CAT("MatchBEPairs", "model"); for (auto& proc : processes_) { for (auto& thread : proc.threads) { std::stack begin_stack; @@ -74,7 +72,6 @@ void TraceModel::build_index(std::function on_progress) { // Remove end events, dedup, compute depth/self-time, build spatial index { - TRACE_SCOPE_CAT("ProcessThreads", "model"); for (auto& proc : processes_) { for (auto& thread : proc.threads) { // Remove matched end events @@ -155,7 +152,6 @@ void TraceModel::build_index(std::function on_progress) { // Sort threads and processes { - TRACE_SCOPE_CAT("SortThreadsAndProcesses", "model"); for (auto& proc : processes_) { std::sort(proc.threads.begin(), proc.threads.end(), [](const ThreadInfo& a, const ThreadInfo& b) { if (a.sort_index != b.sort_index) return a.sort_index < b.sort_index; @@ -170,7 +166,6 @@ void TraceModel::build_index(std::function on_progress) { // Compute global time range, collect unique categories, and build name-to-events index { - TRACE_SCOPE_CAT("ComputeTimeRangeAndCategories", "model"); categories_.clear(); name_to_events_.clear(); std::unordered_set cat_set; @@ -197,7 +192,6 @@ void TraceModel::build_index(std::function on_progress) { // Compute counter series min/max { - TRACE_SCOPE_CAT("ComputeCounterMinMax", "model"); for (auto& cs : counter_series_) { if (cs.points.empty()) continue; std::sort(cs.points.begin(), cs.points.end()); @@ -212,7 +206,6 @@ void TraceModel::build_index(std::function on_progress) { // Cache aggregate stats for diagnostics panel (avoid per-frame O(n) scans) { - TRACE_SCOPE_CAT("CacheDiagStats", "model"); cached_strings_bytes_ = 0; for (const auto& s : strings_) cached_strings_bytes_ += s.capacity(); cached_args_bytes_ = 0; @@ -225,7 +218,6 @@ void TraceModel::build_index(std::function on_progress) { } int32_t TraceModel::find_parent_event(uint32_t event_idx) const { - TRACE_FUNCTION_CAT("model"); if (event_idx >= events_.size()) return -1; return events_[event_idx].parent_idx; } @@ -249,7 +241,6 @@ std::vector TraceModel::build_call_stack(uint32_t event_idx) const { } double TraceModel::compute_self_time(uint32_t event_idx) const { - TRACE_FUNCTION_CAT("model"); if (event_idx >= events_.size()) return 0.0; return events_[event_idx].self_time; } diff --git a/src/parser/trace_parser.cpp b/src/parser/trace_parser.cpp index 17b1696..e1cca13 100644 --- a/src/parser/trace_parser.cpp +++ b/src/parser/trace_parser.cpp @@ -41,7 +41,6 @@ struct SaxHandler : json::json_sax_t { SaxHandler(TraceModel& m, std::function& prog) : model(m), on_progress(prog) {} void finish_event() { - TRACE_FUNCTION_CAT("io"); event_count++; if (on_progress && (event_count & 0xFFFF) == 0 && estimated_events > 0) { float p = std::min(0.99f, (float)event_count / (float)estimated_events); @@ -96,7 +95,7 @@ struct SaxHandler : json::json_sax_t { } void handle_metadata() { - TRACE_FUNCTION_CAT("io"); + TRACE_FUNCTION_CAT("parser"); const std::string& name = model.get_string(current_event.name_idx); auto& proc = model.get_or_create_process(current_event.pid); @@ -153,6 +152,7 @@ struct SaxHandler : json::json_sax_t { // --- SAX callbacks --- bool null() override { + TRACE_FUNCTION_CAT("parser"); if (state == State::InArgs) { args_append("null"); return true; @@ -162,6 +162,7 @@ struct SaxHandler : json::json_sax_t { } bool boolean(bool val) override { + TRACE_FUNCTION_CAT("parser"); if (state == State::InArgs) { args_append(val ? "true" : "false"); return true; @@ -179,7 +180,6 @@ struct SaxHandler : json::json_sax_t { } bool handle_number(double val, const std::string& raw) { - TRACE_FUNCTION_CAT("io"); if (state == State::InArgs) { if (args_depth == 0) { // Top-level arg value - for counter events, capture the value @@ -319,6 +319,7 @@ struct SaxHandler : json::json_sax_t { } bool start_array(std::size_t) override { + TRACE_FUNCTION_CAT("parser"); if (state == State::Skipping) { skip_depth++; return true; @@ -345,6 +346,7 @@ struct SaxHandler : json::json_sax_t { } bool end_array() override { + TRACE_FUNCTION_CAT("parser"); if (state == State::Skipping) { skip_depth--; if (skip_depth == 0) state = State::InEvent; @@ -378,6 +380,7 @@ struct SaxHandler : json::json_sax_t { } bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) override { + TRACE_FUNCTION_CAT("parser"); (void)position; (void)last_token; (void)ex; @@ -385,7 +388,6 @@ struct SaxHandler : json::json_sax_t { } static std::string escape_json_string(const std::string& s) { - TRACE_FUNCTION_CAT("io"); std::string result; result.reserve(s.size()); for (char c : s) { @@ -421,7 +423,7 @@ struct SaxHandler : json::json_sax_t { }; bool TraceParser::parse(const std::string& filepath, TraceModel& model) { - TRACE_SCOPE_CAT("Parse", "io"); + TRACE_FUNCTION_CAT("parser"); std::ifstream file(filepath, std::ios::binary | std::ios::ate); if (!file.is_open()) { error_message_ = "Could not open file: " + filepath; @@ -436,7 +438,6 @@ bool TraceParser::parse(const std::string& filepath, TraceModel& model) { // Read file in chunks to report progress std::string content(file_size, '\0'); { - TRACE_SCOPE_CAT("ReadFile", "io"); constexpr size_t CHUNK = 4 * 1024 * 1024; // 4MB chunks size_t read_so_far = 0; while (read_so_far < file_size) { @@ -463,10 +464,7 @@ bool TraceParser::parse(const std::string& filepath, TraceModel& model) { handler.estimated_events = file_size / 100; bool result; - { - TRACE_SCOPE_CAT("ParseJSON", "io"); - result = json::sax_parse(content, &handler); - } + { result = json::sax_parse(content, &handler); } // Free the raw JSON string before building the index content.clear(); @@ -487,7 +485,7 @@ bool TraceParser::parse(const std::string& filepath, TraceModel& model) { } bool TraceParser::parse_buffer(const char* data, size_t size, TraceModel& model) { - TRACE_SCOPE_CAT("ParseBuffer", "io"); + TRACE_FUNCTION_CAT("parser"); model.clear(); model.intern_string(""); diff --git a/src/platform/file_loader_desktop.cpp b/src/platform/file_loader_desktop.cpp index 103de4c..04d16aa 100644 --- a/src/platform/file_loader_desktop.cpp +++ b/src/platform/file_loader_desktop.cpp @@ -71,6 +71,7 @@ FileLoader::~FileLoader() { } void FileLoader::load_file(const std::string& path, bool time_ns, QueryDb* query_db) { + TRACE_FUNCTION_CAT("platform"); join(); impl_->filename_ = path; @@ -85,8 +86,6 @@ void FileLoader::load_file(const std::string& path, bool time_ns, QueryDb* query impl_->error_.clear(); impl_->thread = std::thread([this, path, time_ns, query_db]() { - TRACE_SCOPE_CAT("OpenFile", "io"); - TraceParser parser; impl_->setup_progress(parser); parser.set_time_unit_ns(time_ns); @@ -109,8 +108,6 @@ void FileLoader::load_buffer(std::vector data, const std::string& filename impl_->error_.clear(); impl_->thread = std::thread([this, data = std::move(data), time_ns, query_db]() { - TRACE_SCOPE_CAT("OpenBuffer", "io"); - TraceParser parser; impl_->setup_progress(parser); parser.set_time_unit_ns(time_ns); @@ -126,6 +123,7 @@ bool FileLoader::is_loading() const { } bool FileLoader::poll_finished() { + TRACE_FUNCTION_CAT("platform"); if (!impl_->finished.load(std::memory_order_acquire)) return false; join(); impl_->loading = false; diff --git a/src/platform/platform_desktop.cpp b/src/platform/platform_desktop.cpp index 207d5fd..331f606 100644 --- a/src/platform/platform_desktop.cpp +++ b/src/platform/platform_desktop.cpp @@ -1,6 +1,7 @@ #include "platform.h" #include #include +#include "tracing.h" static platform::PendingFile g_pending; static bool g_has_pending = false; @@ -12,6 +13,7 @@ static void file_dialog_callback(void* /*userdata*/, const char* const* filelist } void platform::set_gl_attributes() { + TRACE_FUNCTION_CAT("platform"); SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); @@ -39,6 +41,7 @@ void platform::run_main_loop(void (*step)(), bool* running) { } std::string platform::settings_path() { + TRACE_FUNCTION_CAT("platform"); std::string dir; if (const char* xdg = std::getenv("XDG_CONFIG_HOME")) { dir = std::string(xdg) + "/trace-render"; @@ -55,6 +58,7 @@ bool platform::supports_vsync() { } void platform::open_file_dialog(SDL_Window* window) { + TRACE_FUNCTION_CAT("platform"); if (!window) return; static const SDL_DialogFileFilter filters[] = { {"JSON Trace Files", "json"}, @@ -64,6 +68,7 @@ void platform::open_file_dialog(SDL_Window* window) { } void platform::handle_file_drop(const char* path) { + TRACE_FUNCTION_CAT("platform"); g_pending.path = path; g_pending.data.clear(); @@ -80,6 +85,7 @@ bool platform::has_pending_file() { } platform::PendingFile platform::take_pending_file() { + TRACE_FUNCTION_CAT("platform"); g_has_pending = false; return std::move(g_pending); } diff --git a/src/ui/counter_track.cpp b/src/ui/counter_track.cpp index fae382e..600f0ad 100644 --- a/src/ui/counter_track.cpp +++ b/src/ui/counter_track.cpp @@ -6,7 +6,7 @@ float CounterTrackRenderer::render(ImDrawList* dl, ImVec2 area_min, float y_offset, float width, const TraceModel& model, uint32_t pid, const ViewState& view) { - TRACE_FUNCTION_CAT("timeline"); + TRACE_FUNCTION_CAT("ui"); float total_height = 0.0f; uint32_t color_idx = 0; @@ -51,7 +51,7 @@ static float time_to_x(double ts, double view_start, double view_end, float trac std::vector merge_counter_points(const std::vector>& points, double view_start, double view_end, float track_x, float track_w) { - TRACE_FUNCTION_CAT("timeline"); + TRACE_FUNCTION_CAT("ui"); std::vector result; if (points.empty()) return result; @@ -102,7 +102,7 @@ std::vector merge_counter_points(const std::vector= layout.track_max.y) continue; if (mouse_x < layout.track_min.x || mouse_x >= layout.track_max.x) continue; diff --git a/src/ui/detail_panel.cpp b/src/ui/detail_panel.cpp index 3ebbc45..1b1d16f 100644 --- a/src/ui/detail_panel.cpp +++ b/src/ui/detail_panel.cpp @@ -126,7 +126,6 @@ static const char* phase_name(Phase ph) { } static void render_json_value(const nlohmann::json& j, int depth = 0) { - TRACE_FUNCTION_CAT("ui"); if (j.is_object()) { for (auto& [key, val] : j.items()) { if (val.is_object() || val.is_array()) { @@ -157,13 +156,12 @@ static void render_json_value(const nlohmann::json& j, int depth = 0) { } void DetailPanel::render_range_selection(const TraceModel& model, ViewState& view) { - TRACE_SCOPE_CAT("RangeSelection", "ui"); + TRACE_FUNCTION_CAT("ui"); // Defer expensive stats computation while actively drag-selecting; // only compute once the drag finishes. if (!view.range_selecting() && (cached_range_start_ != view.range_start_ts() || cached_range_end_ != view.range_end_ts())) { - TRACE_SCOPE_CAT("ComputeRangeStats", "ui"); cached_range_start_ = view.range_start_ts(); cached_range_end_ = view.range_end_ts(); range_stats_ = compute_range_stats(model, view.range_start_ts(), view.range_end_ts()); @@ -252,7 +250,7 @@ void DetailPanel::render_range_selection(const TraceModel& model, ViewState& vie } void DetailPanel::render(const TraceModel& model, ViewState& view) { - TRACE_SCOPE_CAT("Details", "ui"); + TRACE_FUNCTION_CAT("ui"); ImGui::Begin("Details"); if (view.has_range_selection()) { @@ -413,7 +411,6 @@ void DetailPanel::render(const TraceModel& model, ViewState& view) { // Call Stack tab — rebuild cache if selected event changed if (cached_stack_event_idx_ != view.selected_event_idx()) { - TRACE_SCOPE_CAT("RebuildCallStack", "ui"); cached_stack_event_idx_ = view.selected_event_idx(); cached_call_stack_ = model.build_call_stack(view.selected_event_idx()); @@ -697,7 +694,6 @@ void DetailPanel::render(const TraceModel& model, ViewState& view) { } void DetailPanel::render_aggregated_table(const TraceModel& model, ViewState& view) { - TRACE_FUNCTION_CAT("ui"); if (ImGui::BeginTable("AggChildrenTable", 7, ImGuiTableFlags_Sortable | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable, @@ -817,7 +813,6 @@ void DetailPanel::render_aggregated_table(const TraceModel& model, ViewState& vi } void DetailPanel::render_children_table(const TraceModel& model, ViewState& view) { - TRACE_FUNCTION_CAT("ui"); if (ImGui::BeginTable("ChildrenTable", 3, ImGuiTableFlags_Sortable | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable, @@ -939,7 +934,6 @@ void DetailPanel::rebuild_children(const TraceModel& model, const TraceEvent& ev } void DetailPanel::rebuild_aggregated(const TraceModel& model, double parent_dur) { - TRACE_FUNCTION_CAT("ui"); aggregated_.clear(); // Group by name_idx using a map to accumulator index diff --git a/src/ui/diagnostics_panel.cpp b/src/ui/diagnostics_panel.cpp index 6f18016..eae1f56 100644 --- a/src/ui/diagnostics_panel.cpp +++ b/src/ui/diagnostics_panel.cpp @@ -7,7 +7,6 @@ #include static void format_bytes(size_t bytes, char* buf, size_t buf_size) { - TRACE_FUNCTION_CAT("ui"); if (bytes >= 1024ULL * 1024 * 1024) snprintf(buf, buf_size, "%.1f GB", bytes / (1024.0 * 1024 * 1024)); else if (bytes >= 1024ULL * 1024) diff --git a/src/ui/filter_panel.cpp b/src/ui/filter_panel.cpp index ff57c6e..fdfe02d 100644 --- a/src/ui/filter_panel.cpp +++ b/src/ui/filter_panel.cpp @@ -3,7 +3,7 @@ #include "imgui.h" void FilterPanel::render(const TraceModel& model, ViewState& view) { - TRACE_SCOPE_CAT("Filters", "ui"); + TRACE_FUNCTION_CAT("ui"); ImGui::Begin("Filters"); // Process / Thread tree diff --git a/src/ui/flame_graph_panel.cpp b/src/ui/flame_graph_panel.cpp index 6798d00..8f58dfb 100644 --- a/src/ui/flame_graph_panel.cpp +++ b/src/ui/flame_graph_panel.cpp @@ -81,7 +81,6 @@ void FlameGraphPanel::update_cache_keys(const ViewState& view, size_t event_coun uint32_t FlameGraphPanel::find_or_create_child(FlameTree& tree, uint32_t parent_idx, uint32_t name_idx, uint32_t cat_idx) { - TRACE_FUNCTION_CAT("ui"); for (uint32_t c = tree.nodes[parent_idx].first_child; c != UINT32_MAX; c = tree.nodes[c].next_sibling) { if (tree.nodes[c].name_idx == name_idx && tree.nodes[c].cat_idx == cat_idx) return c; } @@ -97,7 +96,6 @@ uint32_t FlameGraphPanel::find_or_create_child(FlameTree& tree, uint32_t parent_ } uint32_t FlameGraphPanel::find_or_create_root(FlameTree& tree, uint32_t name_idx, uint32_t cat_idx) { - TRACE_FUNCTION_CAT("ui"); for (uint32_t c = tree.first_root; c != UINT32_MAX; c = tree.nodes[c].next_sibling) { if (tree.nodes[c].name_idx == name_idx && tree.nodes[c].cat_idx == cat_idx) return c; } @@ -132,7 +130,6 @@ uint32_t FlameGraphPanel::sort_children(FlameTree& tree, uint32_t first_child) { } void FlameGraphPanel::compute_self_times(FlameTree& tree) { - TRACE_FUNCTION_CAT("ui"); // Reverse iteration: children are always appended after parents in the pool, // so processing in reverse guarantees children are resolved before parents. for (int i = (int)tree.nodes.size() - 1; i >= 0; --i) { @@ -240,7 +237,7 @@ void FlameGraphPanel::rebuild(const TraceModel& model, const ViewState& view) { // --------------------------------------------------------------------------- void FlameGraphPanel::render(const TraceModel& model, ViewState& view) { - TRACE_SCOPE_CAT("FlameGraph", "ui"); + TRACE_FUNCTION_CAT("ui"); ImGui::Begin("Flame Graph"); if (model.events().empty()) { diff --git a/src/ui/flow_renderer.cpp b/src/ui/flow_renderer.cpp index b589297..b229edc 100644 --- a/src/ui/flow_renderer.cpp +++ b/src/ui/flow_renderer.cpp @@ -4,7 +4,7 @@ void FlowRenderer::render(ImDrawList* dl, const TraceModel& model, const ViewState& view, ImVec2 area_min, ImVec2 area_max, float label_width) { - TRACE_FUNCTION_CAT("timeline"); + TRACE_FUNCTION_CAT("ui"); if (!view.show_flows() || track_positions_.empty()) return; float track_left = area_min.x + label_width; diff --git a/src/ui/instance_panel.cpp b/src/ui/instance_panel.cpp index 1d3fd55..20c7630 100644 --- a/src/ui/instance_panel.cpp +++ b/src/ui/instance_panel.cpp @@ -38,6 +38,7 @@ void InstancePanel::select_function_by_name(const std::string& name, const Trace } void InstancePanel::navigate_to_instance(int32_t idx, const TraceModel& model, ViewState& view) { + TRACE_FUNCTION_CAT("ui"); if (idx < 0 || idx >= (int32_t)instances_.size()) return; instance_cursor_ = idx; uint32_t ev_idx = instances_[idx]; @@ -45,7 +46,7 @@ void InstancePanel::navigate_to_instance(int32_t idx, const TraceModel& model, V } void InstancePanel::render(const TraceModel& model, ViewState& view) { - TRACE_SCOPE_CAT("Instances", "ui"); + TRACE_FUNCTION_CAT("ui"); ImGui::Begin("Instances"); if (model.events().empty()) { @@ -56,7 +57,6 @@ void InstancePanel::render(const TraceModel& model, ViewState& view) { // Track external selection changes { - TRACE_SCOPE_CAT("TrackSelection", "ui"); if (view.selected_event_idx() != last_selected_event_ && view.selected_event_idx() >= 0) { last_selected_event_ = view.selected_event_idx(); const auto& ev = model.events()[view.selected_event_idx()]; @@ -102,7 +102,6 @@ void InstancePanel::render(const TraceModel& model, ViewState& view) { ImGui::TableHeadersRow(); { - TRACE_SCOPE_CAT("SortInstances", "ui"); if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) { if (instances_dirty_) { sort_specs->SpecsDirty = true; @@ -151,7 +150,6 @@ void InstancePanel::render(const TraceModel& model, ViewState& view) { scroll_to_top_ = false; } - TRACE_SCOPE_CAT("DrawRows", "ui"); char buf[64]; ImGuiListClipper clipper; clipper.Begin((int)instances_.size()); diff --git a/src/ui/key_bindings.cpp b/src/ui/key_bindings.cpp index aa0582f..2b434b0 100644 --- a/src/ui/key_bindings.cpp +++ b/src/ui/key_bindings.cpp @@ -28,7 +28,6 @@ void KeyBindings::reset_defaults() { } bool KeyBindings::is_pressed(Action action) const { - TRACE_FUNCTION_CAT("ui"); int idx = static_cast(action); const auto& b = bindings_[idx]; if (b.primary != ImGuiKey_None && ImGui::IsKeyChordPressed(b.primary)) return true; @@ -37,7 +36,6 @@ bool KeyBindings::is_pressed(Action action) const { } const char* KeyBindings::action_name(Action action) { - TRACE_FUNCTION_CAT("ui"); switch (action) { case Action::PanLeft: return "Pan Left"; @@ -91,7 +89,6 @@ std::string KeyBindings::key_chord_name(ImGuiKeyChord chord) { } const char* KeyBindings::action_id(Action action) { - TRACE_FUNCTION_CAT("ui"); switch (action) { case Action::PanLeft: return "pan_left"; @@ -133,7 +130,6 @@ const char* KeyBindings::action_id(Action action) { } bool KeyBindings::is_modifier_key(ImGuiKey key) { - TRACE_FUNCTION_CAT("ui"); return key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl || key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift || key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt || key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper || key == ImGuiKey_ReservedForModCtrl || diff --git a/src/ui/range_stats.cpp b/src/ui/range_stats.cpp index 7b14caf..122c39a 100644 --- a/src/ui/range_stats.cpp +++ b/src/ui/range_stats.cpp @@ -1,8 +1,10 @@ #include "range_stats.h" #include #include +#include "tracing.h" RangeStats compute_range_stats(const TraceModel& model, double start_ts, double end_ts) { + TRACE_FUNCTION_CAT("ui"); RangeStats stats; stats.range_duration = end_ts - start_ts; diff --git a/src/ui/search_panel.cpp b/src/ui/search_panel.cpp index 9dcb1ce..4b6e42e 100644 --- a/src/ui/search_panel.cpp +++ b/src/ui/search_panel.cpp @@ -21,7 +21,7 @@ void SearchPanel::on_model_changed() { } void SearchPanel::render(const TraceModel& model, ViewState& view) { - TRACE_SCOPE_CAT("Search", "ui"); + TRACE_FUNCTION_CAT("ui"); ImGui::Begin("Search"); if (view.key_bindings().is_pressed(Action::Search)) { diff --git a/src/ui/source_panel.cpp b/src/ui/source_panel.cpp index 8f99d78..1eb5955 100644 --- a/src/ui/source_panel.cpp +++ b/src/ui/source_panel.cpp @@ -13,7 +13,7 @@ using json = nlohmann::json; // Try common field names for source file and line in event args bool extract_source_location(const TraceModel& model, const TraceEvent& ev, std::string& file, int& line) { - TRACE_FUNCTION_CAT("io"); + TRACE_FUNCTION_CAT("ui"); if (ev.args_idx == UINT32_MAX || ev.args_idx >= model.args().size()) return false; try { @@ -52,7 +52,7 @@ bool extract_source_location(const TraceModel& model, const TraceEvent& ev, std: } static std::string normalize_slashes(const std::string& path) { - TRACE_FUNCTION_CAT("io"); + TRACE_FUNCTION_CAT("ui"); std::string out = path; for (auto& c : out) { if (c == '\\') c = '/'; @@ -62,7 +62,7 @@ static std::string normalize_slashes(const std::string& path) { std::string remap_source_path(const std::string& trace_path, const std::string& strip_prefix, const std::string& local_base) { - TRACE_FUNCTION_CAT("io"); + TRACE_FUNCTION_CAT("ui"); std::string path = normalize_slashes(trace_path); std::string strip = normalize_slashes(strip_prefix); std::string base = normalize_slashes(local_base); @@ -149,7 +149,7 @@ void SourcePanel::render_settings() { } void SourcePanel::load_file(const std::string& path) { - TRACE_FUNCTION_CAT("io"); + TRACE_FUNCTION_CAT("ui"); cached_lines_.clear(); cached_error_.clear(); @@ -166,7 +166,7 @@ void SourcePanel::load_file(const std::string& path) { } void SourcePanel::resolve_and_load(const std::string& raw_file) { - TRACE_FUNCTION_CAT("io"); + TRACE_FUNCTION_CAT("ui"); std::string full_path = remap_source_path(raw_file, strip_prefix_, local_base_); if (full_path != cached_file_) { @@ -211,7 +211,7 @@ void SourcePanel::build_gutter_text() { } void SourcePanel::render(const TraceModel& model, ViewState& view) { - TRACE_SCOPE_CAT("Source", "ui"); + TRACE_FUNCTION_CAT("ui"); ImGui::Begin("Source"); // Path remapping inputs diff --git a/src/ui/stats_panel.cpp b/src/ui/stats_panel.cpp index a3b4ca3..480d5a3 100644 --- a/src/ui/stats_panel.cpp +++ b/src/ui/stats_panel.cpp @@ -34,7 +34,6 @@ static void render_cell(const std::string& value, bool is_time) { } void StatsPanel::ensure_default_tab() { - TRACE_FUNCTION_CAT("ui"); if (tabs_.empty()) { QueryTab tab; tab.title = "Hot Functions"; @@ -76,7 +75,7 @@ void StatsPanel::load_tabs(const nlohmann::json& j) { } void StatsPanel::render(const TraceModel& model, QueryDb& db, ViewState& view) { - TRACE_SCOPE_CAT("Statistics", "ui"); + TRACE_FUNCTION_CAT("ui"); ImGui::Begin("Statistics"); if (!db.is_loaded()) { @@ -534,6 +533,7 @@ static const int NUM_OPS = 11; static const char* LOGIC_NAMES[] = {"AND", "OR"}; void QueryBuilderState::reset() { + TRACE_FUNCTION_CAT("ui"); table_idx = 0; select_cols.clear(); where_clauses.clear(); @@ -545,6 +545,7 @@ void QueryBuilderState::reset() { } std::string QueryBuilderState::build_sql(const char* const* columns, int num_columns) const { + TRACE_FUNCTION_CAT("ui"); std::string sql = "SELECT "; // SELECT diff --git a/src/ui/timeline_view.cpp b/src/ui/timeline_view.cpp index 34fc87b..a6fd87d 100644 --- a/src/ui/timeline_view.cpp +++ b/src/ui/timeline_view.cpp @@ -23,7 +23,7 @@ static inline int ctz32(uint32_t x) { #endif void TimelineView::render_time_ruler(ImDrawList* dl, ImVec2 area_min, ImVec2 area_max, const ViewState& view) { - TRACE_SCOPE_CAT("TimeRuler", "timeline"); + TRACE_FUNCTION_CAT("ui"); float ruler_height = view.ruler_height(); float width = area_max.x - area_min.x; double range = view.view_end_ts() - view.view_start_ts(); @@ -76,7 +76,6 @@ void TimelineView::render_time_ruler(ImDrawList* dl, ImVec2 area_min, ImVec2 are void TimelineView::render_tracks(ImDrawList* dl, ImVec2 area_min, ImVec2 area_max, const TraceModel& model, ViewState& view) { - TRACE_SCOPE_CAT("RenderTracks", "timeline"); diag_stats = {}; float ruler_height = view.ruler_height(); float width = area_max.x - area_min.x; @@ -319,7 +318,6 @@ void TimelineView::render_tracks(ImDrawList* dl, ImVec2 area_min, ImVec2 area_ma // Counter tracks for this process { - TRACE_SCOPE_CAT("RenderTracks_counters", "timeline"); float counter_h = counter_renderer_.render(dl, area_min, y, width, model, proc.pid, view); y += counter_h; } @@ -363,7 +361,7 @@ void TimelineView::render_tracks(ImDrawList* dl, ImVec2 area_min, ImVec2 area_ma int32_t TimelineView::hit_test(float click_x, float click_y, ImVec2 area_min, ImVec2 area_max, const TraceModel& model, const ViewState& view) { - TRACE_FUNCTION_CAT("timeline"); + TRACE_FUNCTION_CAT("ui"); float track_left = area_min.x + view.label_width(); float track_width = (area_max.x - area_min.x) - view.label_width(); @@ -414,7 +412,7 @@ int32_t TimelineView::hit_test(float click_x, float click_y, ImVec2 area_min, Im } void TimelineView::render(const TraceModel& model, ViewState& view) { - TRACE_SCOPE_CAT("Timeline", "ui"); + TRACE_FUNCTION_CAT("ui"); ImGui::Begin("Timeline", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); ImVec2 canvas_min = ImGui::GetCursorScreenPos(); diff --git a/src/ui/toolbar.cpp b/src/ui/toolbar.cpp index 4c2264f..b699c47 100644 --- a/src/ui/toolbar.cpp +++ b/src/ui/toolbar.cpp @@ -5,7 +5,7 @@ #include void Toolbar::render(const TraceModel& model, ViewState& view, float rss_mb) { - TRACE_SCOPE_CAT("Toolbar", "ui"); + TRACE_FUNCTION_CAT("ui"); if (ImGui::BeginMainMenuBar()) { ImGui::TextDisabled("%.0f FPS", ImGui::GetIO().Framerate); ImGui::SameLine();