summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-07 09:47:05 +0300
committerPaul Buetow <paul@buetow.org>2026-05-07 09:47:05 +0300
commited04ccd8e2297458ec97381806a05dea13090f0f (patch)
tree99b707441b6990686bbd2773c177bdc3b8bf3313 /internal
parentac05beabdbaa90597ed0cfcea781d5ab44d123ba (diff)
update docs and ascii banner
Diffstat (limited to 'internal')
-rw-r--r--internal/flags/flags.go4
-rw-r--r--internal/flags/version.go13
-rw-r--r--internal/ior.go5
-rw-r--r--internal/ior_mode_test.go12
4 files changed, 29 insertions, 5 deletions
diff --git a/internal/flags/flags.go b/internal/flags/flags.go
index 9b1c3cf..5d00b74 100644
--- a/internal/flags/flags.go
+++ b/internal/flags/flags.go
@@ -42,6 +42,9 @@ type Config struct {
CollapsedFields []string
CountField string
GlobalFilter globalfilter.Filter
+
+ // ShowVersion prints the banner plus version and exits without running.
+ ShowVersion bool
}
var (
@@ -177,6 +180,7 @@ func parse() error {
flag.BoolVar(&cfg.TestLiveFlames, "testliveflames", false, "Run TUI with continuously-updating synthetic flamegraph data for live keyboard-navigation testing")
flag.DurationVar(&cfg.LiveInterval, "live-interval", cfg.LiveInterval, "Synthetic live flamegraph refresh interval for --testliveflames")
flag.BoolVar(&cfg.TUIExportEnable, "tuiExport", cfg.TUIExportEnable, "Enable TUI CSV snapshot export files (separate from Parquet recording)")
+ flag.BoolVar(&cfg.ShowVersion, "version", false, "Print version banner and exit")
fields := flag.String("fields", "",
fmt.Sprintf("Comma separated list of fields to collapse, valid are: %v", validFields))
flag.StringVar(&cfg.CountField, "count", cfg.CountField,
diff --git a/internal/flags/version.go b/internal/flags/version.go
index 3db0b9f..583d19b 100644
--- a/internal/flags/version.go
+++ b/internal/flags/version.go
@@ -5,11 +5,14 @@ import "fmt"
// Version is the current application version.
const Version = "v0.0.1"
-const asciiBannerTemplate = ` ___ _____ ___ _ _
-|_ _| / / _ \ | _ (_)___| |_
- | | / / (_) | | / / _ \ _|
-|___/_/ \___/ |_|_\_\___/\__| NG
- %s`
+const asciiBannerTemplate = ` ██╗ ██╗ ██████╗ ██████╗ ██╗ ██████╗ ████████╗
+ ██║ ██╔╝ ██╔═══██╗ ██╔══██╗ ██║ ██╔═══██╗ ╚══██╔══╝
+ ██║ ██╔╝ ██║ ██║ ██████╔╝ ██║ ██║ ██║ ██║
+ ██║ ██╔╝ ██║ ██║ ██╔══██╗ ██║ ██║ ██║ ██║
+ ██║██╔╝ ╚██████╔╝ ██║ ██║ ██║ ╚██████╔╝ ██║
+ ╚═╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝
+ ⚡ Next-Generation BPF I/O Syscall Tracer ⚡
+ %s`
// PrintVersion prints the banner with the current version.
func PrintVersion() {
diff --git a/internal/ior.go b/internal/ior.go
index e6e7233..63da223 100644
--- a/internal/ior.go
+++ b/internal/ior.go
@@ -57,6 +57,11 @@ func dispatchRun(cfg flags.Config) error {
if cfg.TestLiveFlames {
return runTUITestLiveFlamesFn(cfg, tuiTestLiveFlamesStarter(cfg))
}
+ // All remaining modes require tracing, which needs root. Fail fast here so
+ // the TUI never starts (and hangs) when we already know it cannot trace.
+ if getEUID() != 0 {
+ return errRootPrivilegesRequired
+ }
if isHeadlessParquetMode(cfg) {
return runParquetFn(cfg)
}
diff --git a/internal/ior_mode_test.go b/internal/ior_mode_test.go
index a7fcab1..ab0466d 100644
--- a/internal/ior_mode_test.go
+++ b/internal/ior_mode_test.go
@@ -95,13 +95,16 @@ func TestDispatchRunUsesTraceModeWhenRequested(t *testing.T) {
origRunTUI := runTUIFn
origRunTUITestFlames := runTUITestFlamesFn
origRunTUITestLiveFlames := runTUITestLiveFlamesFn
+ origGetEUID := getEUID
defer func() {
runTraceFn = origRunTrace
runParquetFn = origRunParquet
runTUIFn = origRunTUI
runTUITestFlamesFn = origRunTUITestFlames
runTUITestLiveFlamesFn = origRunTUITestLiveFlames
+ getEUID = origGetEUID
}()
+ getEUID = func() int { return 0 }
traceCalled := false
tuiCalled := false
@@ -144,13 +147,16 @@ func TestDispatchRunUsesHeadlessParquetModeWhenRequested(t *testing.T) {
origRunTUI := runTUIFn
origRunTUITestFlames := runTUITestFlamesFn
origRunTUITestLiveFlames := runTUITestLiveFlamesFn
+ origGetEUID := getEUID
defer func() {
runTraceFn = origRunTrace
runParquetFn = origRunParquet
runTUIFn = origRunTUI
runTUITestFlamesFn = origRunTUITestFlames
runTUITestLiveFlamesFn = origRunTUITestLiveFlames
+ getEUID = origGetEUID
}()
+ getEUID = func() int { return 0 }
traceCalled := false
parquetCalled := false
@@ -196,12 +202,15 @@ func TestDispatchRunUsesTUIWhenOnlyPprofEnabled(t *testing.T) {
origRunTUI := runTUIFn
origRunTUITestFlames := runTUITestFlamesFn
origRunTUITestLiveFlames := runTUITestLiveFlamesFn
+ origGetEUID := getEUID
defer func() {
runTraceFn = origRunTrace
runTUIFn = origRunTUI
runTUITestFlamesFn = origRunTUITestFlames
runTUITestLiveFlamesFn = origRunTUITestLiveFlames
+ getEUID = origGetEUID
}()
+ getEUID = func() int { return 0 }
traceCalled := false
tuiCalled := false
@@ -239,12 +248,15 @@ func TestDispatchRunUsesTUIStarterWhenNotPlain(t *testing.T) {
origRunTUI := runTUIFn
origRunTUITestFlames := runTUITestFlamesFn
origRunTUITestLiveFlames := runTUITestLiveFlamesFn
+ origGetEUID := getEUID
defer func() {
runTraceWithContextFn = origRunTraceWithContext
runTUIFn = origRunTUI
runTUITestFlamesFn = origRunTUITestFlames
runTUITestLiveFlamesFn = origRunTUITestLiveFlames
+ getEUID = origGetEUID
}()
+ getEUID = func() int { return 0 }
traceDone := make(chan struct{}, 1)
runTraceWithContextFn = func(_ context.Context, _ flags.Config, started chan<- struct{}, configure func(*eventLoop)) error {