summaryrefslogtreecommitdiff
path: root/internal/tui/flamegraph/controls.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-06 15:21:01 +0200
committerPaul Buetow <paul@buetow.org>2026-03-06 15:21:01 +0200
commit4ff17c30120d657b966f8a55188ba167dc875e64 (patch)
tree62737caf6b8e7411c2437dd995d3de5ce6aeca99 /internal/tui/flamegraph/controls.go
parent1530bf2856bbb32a6e0457596b55c07f3836a0ec (diff)
feat(tui): add flamegraph bytes metric toggle
Diffstat (limited to 'internal/tui/flamegraph/controls.go')
-rw-r--r--internal/tui/flamegraph/controls.go69
1 files changed, 46 insertions, 23 deletions
diff --git a/internal/tui/flamegraph/controls.go b/internal/tui/flamegraph/controls.go
index cd74df5..06e6d0d 100644
--- a/internal/tui/flamegraph/controls.go
+++ b/internal/tui/flamegraph/controls.go
@@ -13,10 +13,7 @@ func (m *Model) togglePause() {
m.paused = !m.paused
}
-func (m *Model) resetBaseline() {
- if m.liveTrie != nil {
- m.liveTrie.Reset()
- }
+func (m *Model) clearSnapshotState(clearSearch bool) {
m.zoomRoot = nil
m.zoomPath = ""
m.zoomStack = nil
@@ -25,11 +22,20 @@ func (m *Model) resetBaseline() {
m.globalTotal = 0
m.frames = nil
m.targetFrames = nil
- m.searchQuery = ""
m.matchIndices = make(map[int]bool)
m.filterVisible = make(map[int]bool)
m.subtreeSet = make(map[int]bool)
m.hasNavigableSnapshot = false
+ if clearSearch {
+ m.searchQuery = ""
+ }
+}
+
+func (m *Model) resetBaseline() {
+ if m.liveTrie != nil {
+ m.liveTrie.Reset()
+ }
+ m.clearSnapshotState(true)
m.statusMessage = "Baseline reset"
}
@@ -45,21 +51,26 @@ func (m *Model) cycleFieldOrder() {
return
}
}
- m.zoomRoot = nil
- m.zoomPath = ""
- m.zoomStack = nil
- m.selectedIdx = 0
- m.snapshot = nil
- m.globalTotal = 0
- m.frames = nil
- m.targetFrames = nil
- m.matchIndices = make(map[int]bool)
- m.filterVisible = make(map[int]bool)
- m.subtreeSet = make(map[int]bool)
- m.hasNavigableSnapshot = false
+ m.clearSnapshotState(false)
m.statusMessage = "Order: " + strings.Join(nextPreset, "/")
}
+func (m *Model) toggleCountField() {
+ next := "bytes"
+ if m.countField == "bytes" {
+ next = "count"
+ }
+ if m.liveTrie != nil {
+ if err := m.liveTrie.SetCountField(next); err != nil {
+ m.statusMessage = "Metric toggle error: " + err.Error()
+ return
+ }
+ }
+ m.countField = next
+ m.clearSnapshotState(false)
+ m.statusMessage = "Metric: " + m.countFieldLabel() + " (new baseline)"
+}
+
func (m *Model) toggleHelp() {
m.showHelp = !m.showHelp
}
@@ -70,7 +81,7 @@ func (m Model) toolbarLine() string {
state = lipgloss.NewStyle().Foreground(common.ColorDanger).Bold(true).Render("[PAUSED]")
}
order := m.currentFieldPresetLabel()
- line := fmt.Sprintf("%s | view:%s | o:order(%s) | /:search | enter:zoom | u/esc:undo | r:reset | space/p:pause", state, compactFramePath(m.currentRootPath()), order)
+ line := fmt.Sprintf("%s | view:%s | o:order(%s) | b:metric(%s) | /:search | enter:zoom | u/esc:undo | r:reset | space/p:pause", state, compactFramePath(m.currentRootPath()), order, m.countFieldLabel())
if m.searchQuery != "" {
line += " | filter:" + m.searchQuery
}
@@ -92,7 +103,7 @@ func (m Model) helpOverlay() string {
if width <= 0 {
width = 80
}
- help := "Flame help: j/k depth h/l sibling pgup top pgdn root enter zoom u/backspace/esc undo / search n/N matches space/p pause r reset baseline o order ? help"
+ help := "Flame help: j/k depth h/l sibling pgup top pgdn root enter zoom u/backspace/esc undo / search n/N matches space/p pause r reset baseline o order b metric ? help"
return common.HelpBarStyle.Width(width).Render(padOrTrim(help, width))
}
@@ -118,17 +129,18 @@ func (m Model) selectionStatusLine() string {
if m.globalTotal > 0 {
systemShare = percentOfTotal(frame.Total, m.globalTotal)
}
- shareLabel := fmt.Sprintf("%.2f%% system", systemShare)
+ metric := m.countFieldLabel()
+ shareLabel := fmt.Sprintf("%.2f%% of total %s", systemShare, metric)
if strings.TrimSpace(m.searchQuery) != "" && len(m.matchIndices) > 0 {
filterTotal, _ := filterCoverageTotals(m.frames, m.matchIndices, m.globalTotal)
if filterTotal > 0 {
selectedFilterTotal := filterCoverageTotalForPath(m.frames, m.matchIndices, frame.Path)
filterShare := percentOfTotal(selectedFilterTotal, filterTotal)
- shareLabel = fmt.Sprintf("%.2f%% filter", filterShare)
+ shareLabel = fmt.Sprintf("%.2f%% of filtered %s", filterShare, metric)
}
}
- line := fmt.Sprintf("[%s] sel:%d/%d %s | path:%s | depth:%d | total:%d | %s",
- mode, selIdx+1, len(m.frames), frame.Name, compactFramePath(frame.Path), frame.Depth, frame.Total, shareLabel)
+ line := fmt.Sprintf("[%s] sel:%d/%d %s | path:%s | depth:%d | total(%s):%d | %s",
+ mode, selIdx+1, len(m.frames), frame.Name, compactFramePath(frame.Path), frame.Depth, m.countFieldLabel(), frame.Total, shareLabel)
if m.searchQuery != "" {
line += " | filter:" + m.searchQuery
}
@@ -148,3 +160,14 @@ func (m Model) currentFieldPresetLabel() string {
}
return strings.Join(m.fieldPresets[idx], "/")
}
+
+func (m Model) countFieldLabel() string {
+ switch m.countField {
+ case "count":
+ return "events"
+ case "bytes":
+ return "bytes"
+ default:
+ return m.countField
+ }
+}