Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,176 @@
|
||||
// Copyright (c) 2020-2024 Joe Conigliaro. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module main
|
||||
|
||||
import gg
|
||||
import v2.profiler
|
||||
|
||||
// Filter modes for allocation view
|
||||
pub enum FilterMode {
|
||||
all // Show all allocations
|
||||
new_not_freed // Show allocations that haven't been freed (potential leaks)
|
||||
large // Show allocations larger than threshold
|
||||
current_frame // Show allocations from selected frame only
|
||||
}
|
||||
|
||||
// App holds the GUI application state
|
||||
pub struct App {
|
||||
pub mut:
|
||||
gg &gg.Context = unsafe { nil }
|
||||
// View state
|
||||
selected_frame int = -1 // -1 means no selection
|
||||
selected_alloc int = -1 // Index of selected allocation
|
||||
filter_mode FilterMode = .all
|
||||
large_threshold int = 1024 // Threshold for "large" filter (bytes)
|
||||
search_text string
|
||||
// Scroll/pan state
|
||||
timeline_scroll int // Horizontal scroll for timeline
|
||||
histogram_scroll int // Scroll for histogram if needed
|
||||
// Cached data (updated each frame)
|
||||
cached_frames []profiler.FrameData
|
||||
cached_allocs []profiler.AllocRecord
|
||||
cached_stats profiler.Statistics
|
||||
// Layout dimensions
|
||||
header_height int = 60
|
||||
histogram_height int = 200
|
||||
timeline_height int = 80
|
||||
controls_height int = 40
|
||||
details_height int = 60
|
||||
// Hover state
|
||||
hover_frame int = -1
|
||||
hover_alloc int = -1
|
||||
// Mode
|
||||
demo_mode bool // Use simulated data for demo (false = live mode)
|
||||
demo_frame int // Current demo frame counter
|
||||
live_mode bool = true // Read from profiler snapshot file
|
||||
// Status
|
||||
status_msg string = 'Waiting for profiler data...'
|
||||
}
|
||||
|
||||
// Colors for the profiler visualization
|
||||
pub const bg_color = gg.Color{30, 30, 30, 255} // Dark gray background
|
||||
pub const new_alloc_color = gg.Color{0, 200, 255, 200} // Cyan for new allocations
|
||||
pub const freed_color = gg.Color{180, 100, 255, 200} // Purple for freed allocations
|
||||
pub const leak_color = gg.Color{255, 100, 100, 255} // Red for leaks/live
|
||||
pub const timeline_cursor = gg.Color{255, 255, 0, 255} // Yellow for timeline cursor
|
||||
pub const text_color = gg.Color{220, 220, 220, 255} // Light gray text
|
||||
pub const header_bg = gg.Color{40, 40, 45, 255} // Slightly lighter header
|
||||
pub const grid_color = gg.Color{60, 60, 60, 255} // Grid lines
|
||||
pub const selected_color = gg.Color{100, 150, 255, 255} // Selection highlight
|
||||
|
||||
// Window dimensions
|
||||
pub const window_width = 1200
|
||||
pub const window_height = 700
|
||||
|
||||
// init_app creates a new App instance (unused, kept for reference)
|
||||
fn init_app_heap() &App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
// update_cache refreshes cached profiler data
|
||||
pub fn (mut app App) update_cache() {
|
||||
if app.demo_mode {
|
||||
return
|
||||
}
|
||||
|
||||
if app.live_mode {
|
||||
// Read from snapshot file
|
||||
if snap := profiler.read_snapshot() {
|
||||
app.cached_frames = snap.frames
|
||||
app.cached_allocs = snap.allocs
|
||||
app.cached_stats = profiler.Statistics{
|
||||
live_bytes: snap.live_bytes
|
||||
peak_bytes: snap.peak_bytes
|
||||
total_allocs: snap.total_allocs
|
||||
total_frees: snap.total_frees
|
||||
leak_count: count_leaks(snap.allocs)
|
||||
frame_count: snap.frame_count
|
||||
}
|
||||
app.status_msg = 'Live: Frame ${snap.frame_count}'
|
||||
} else {
|
||||
app.status_msg = 'Waiting for profiler data at ${profiler.profiler_data_path}...'
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// In-process mode (same process)
|
||||
app.cached_frames = profiler.get_frames()
|
||||
app.cached_allocs = profiler.get_allocs()
|
||||
app.cached_stats = profiler.get_statistics()
|
||||
}
|
||||
|
||||
fn count_leaks(allocs []profiler.AllocRecord) int {
|
||||
mut count := 0
|
||||
for alloc in allocs {
|
||||
if !alloc.freed {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// get_filtered_allocs returns allocations matching current filter
|
||||
pub fn (app &App) get_filtered_allocs() []profiler.AllocRecord {
|
||||
mut result := []profiler.AllocRecord{}
|
||||
|
||||
allocs := app.cached_allocs
|
||||
|
||||
for alloc in allocs {
|
||||
match app.filter_mode {
|
||||
.all {
|
||||
result << alloc
|
||||
}
|
||||
.new_not_freed {
|
||||
if !alloc.freed {
|
||||
result << alloc
|
||||
}
|
||||
}
|
||||
.large {
|
||||
if alloc.size >= app.large_threshold {
|
||||
result << alloc
|
||||
}
|
||||
}
|
||||
.current_frame {
|
||||
if app.selected_frame >= 0 && alloc.frame == u64(app.selected_frame) {
|
||||
result << alloc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply search filter if text is present
|
||||
if app.search_text.len > 0 {
|
||||
mut filtered := []profiler.AllocRecord{}
|
||||
for alloc in result {
|
||||
if alloc.file.contains(app.search_text) {
|
||||
filtered << alloc
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// get_frame_at_x returns the frame index at the given x coordinate in timeline
|
||||
pub fn (app &App) get_frame_at_x(x f32, timeline_x f32, timeline_width f32) int {
|
||||
frames := app.cached_frames
|
||||
if frames.len == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Calculate which frame this x position corresponds to
|
||||
relative_x := x - timeline_x
|
||||
if relative_x < 0 || relative_x > timeline_width {
|
||||
return -1
|
||||
}
|
||||
|
||||
frame_width := timeline_width / f32(frames.len)
|
||||
frame_idx := int(relative_x / frame_width)
|
||||
|
||||
if frame_idx >= 0 && frame_idx < frames.len {
|
||||
return frame_idx
|
||||
}
|
||||
return -1
|
||||
}
|
||||
Reference in new issue
Block a user