lib/tracy/src/cli/usage.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const pretty = @import("pretty");
3 const pretty_usage = @import("pretty_usage");
4
5 const usage_commands = [_][]const u8{
6 "tracy-agent summary <events.jsonl> [--format text|jsonl] [--top N]",
7 "tracy-agent tree <events.jsonl> [--format text|jsonl] [--top N] [--max-depth N] [--min-duration-ns N] [--thread ID]",
8 "tracy-agent stats <events.jsonl> [--format text|jsonl] [--top N] [--sort self|total|mean|count|threads|name] [--min-total-ns N] [--min-self-ns N] [--thread ID]",
9 "tracy-agent frames <events.jsonl> [--format text|jsonl] [--top N]" ++
10 " [--sort max|total|mean|count|name] [--min-frame-ns N]" ++
11 " [--budget-ns N] [--set NAME]",
12 "tracy-agent memory <events.jsonl> [--format text|jsonl] [--top N]" ++
13 " [--occurrences N] [--sort live|high-water|allocated|lifetime-tail|allocs" ++
14 "|frees|name] [--min-live-bytes N] [--name NAME]",
15 "tracy-agent locks <events.jsonl> [--format text|jsonl] [--top N]" ++
16 " [--occurrences N] [--sort wait|wait-tail|hold|hold-tail|contention" ++
17 "|count|name|last] [--kind all|wait|hold] [--mode all|lock|shared]" ++
18 " [--min-wait-ns N] [--min-hold-ns N] [--lock ID] [--thread ID]" ++
19 " [--since-ns N] [--until-ns N] [--around-ns N] [--window-ns N]" ++
20 " [--match TEXT] [--ignore-case]",
21 "tracy-agent messages <events.jsonl> [--format text|jsonl] [--top N] [--occurrences N] [--kind all|message|app-info] [--group thread|text|kind|none] [--sort time|count|last|thread|text|kind] [--thread ID] [--since-ns N] [--until-ns N] [--around-ns N] [--window-ns N] [--match TEXT] [--ignore-case]",
22 "tracy-agent samples <events.jsonl> [--format text|jsonl] [--top N] [--occurrences N] [--group source|symbol|stack|thread|kind|none] [--sort samples|weight|last|first|thread|name|kind] [--kind TEXT] [--min-weight N] [--thread ID] [--since-ns N] [--until-ns N] [--around-ns N] [--window-ns N] [--match TEXT] [--ignore-case]",
23 "tracy-agent fibers <events.jsonl> [--format text|jsonl] [--top N] [--occurrences N] [--group fiber|thread|transition|none] [--sort running|enters|leaves|migrations|unmatched|last|fiber|thread|label] [--fiber ID] [--thread ID] [--since-ns N] [--until-ns N] [--around-ns N] [--window-ns N] [--match TEXT] [--ignore-case]",
24 "tracy-agent gpu <events.jsonl> [--format text|jsonl] [--top N]" ++
25 " [--occurrences N] [--group context|name|thread|annotation|none]" ++
26 " [--sort gpu|gpu-tail|cpu|cpu-tail|count|annotations|last|context|thread|label]" ++
27 " [--context ID] [--thread ID] [--since-ns N] [--until-ns N]" ++
28 " [--around-ns N] [--window-ns N] [--min-gpu-ns N] [--match TEXT]" ++
29 " [--ignore-case]",
30 "tracy-agent context <events.jsonl> [--format text|jsonl] [--top N]" ++
31 " [--occurrences N] [--group thread|cpu|reason|state|transition|none]" ++
32 " [--sort running|blocked|ready|latency|switches|wakeups|migrations|last" ++
33 "|thread|cpu|label] [--thread ID] [--cpu ID] [--since-ns N]" ++
34 " [--until-ns N] [--around-ns N] [--window-ns N] [--match TEXT]" ++
35 " [--ignore-case]",
36 "tracy-agent system <events.jsonl> [--format text|jsonl] [--top N] [--occurrences N] [--group kind|thread|cpu|address|topology|name|none] [--sort count|value|last|first|thread|cpu|address|label] [--thread ID] [--cpu ID] [--address ADDR] [--kind TEXT] [--since-ns N] [--until-ns N] [--around-ns N] [--window-ns N] [--match TEXT] [--ignore-case]",
37 "tracy-agent plots <events.jsonl> [--format text|jsonl] [--top N] [--sort samples|max|min|mean|range|last|name] [--min-samples N] [--name NAME]",
38 "tracy-agent source <events.jsonl> [--format text|jsonl] [--top N] [--sort self|total|mean|count|file|name] [--min-total-ns N] [--min-self-ns N] [--thread ID] [--match TEXT] [--root PATH] [--context N] [--context-back N] [--ignore-case]",
39 "tracy-agent find <events.jsonl> PATTERN [--format text|jsonl] [--top N] [--occurrences N] [--group source|name|thread|parent|text|none] [--sort total|self|count|mean|max|name|time] [--thread ID] [--since-ns N] [--until-ns N] [--around-ns N] [--window-ns N] [--min-duration-ns N] [--ignore-case]",
40 "tracy-agent flame <events.jsonl> [--format text|jsonl|folded] [--top N] [--sort time|name] [--max-depth N] [--min-ns N] [--thread ID] [--since-ns N] [--until-ns N] [--around-ns N] [--window-ns N] [--match TEXT]",
41 "tracy-agent timeline <events.jsonl> [--format text|jsonl] [--top N] [--kind all|zone|message|app-info|frame|plot|alloc|free|thread|fiber|gpu|sample|context|lock|metadata] [--sort time|duration] [--thread ID] [--since-ns N] [--until-ns N] [--around-ns N] [--window-ns N] [--min-duration-ns N] [--match TEXT]",
42 "tracy-agent compare BASELINE.jsonl RUN.jsonl [--format text|jsonl] [--top N]" ++
43 " [--frame-budget-ns N] [--min-frame-delta-ns N]" ++
44 " [--min-zone-delta-ns N] [--min-gpu-delta-ns N]" ++
45 " [--min-memory-delta-bytes N] [--min-memory-lifetime-delta-ns N]" ++
46 " [--include-stable]",
47 "tracy-agent export zones|messages|plots|allocs <events.jsonl> [--output PATH]",
48 };
49
50 pub fn printError(allocator: std.mem.Allocator, file: std.Io.File, text: []const u8) !void {
51 try pretty_usage.Terminal.init(allocator, file, .{}).writeErrorText("tracy-agent", text);
52 }
53
54 pub fn write(allocator: std.mem.Allocator, file: std.Io.File) !void {
55 var arena_state = std.heap.ArenaAllocator.init(allocator);
56 defer arena_state.deinit();
57 const builder = pretty.Builder.init(arena_state.allocator());
58 try pretty_usage.Terminal.init(allocator, file, .{}).writeDoc(try docAlloc(builder));
59 }
60
61 fn renderAlloc(allocator: std.mem.Allocator, options: pretty.LayoutOptions) ![]u8 {
62 var arena_state = std.heap.ArenaAllocator.init(allocator);
63 defer arena_state.deinit();
64 const builder = pretty.Builder.init(arena_state.allocator());
65 const doc = try docAlloc(builder);
66 return try pretty.renderAlloc(allocator, doc, options);
67 }
68
69 fn docAlloc(builder: pretty.Builder) !pretty.Doc {
70 var parts: std.ArrayListUnmanaged(pretty.Doc) = .empty;
71 try parts.append(builder.allocator, try builder.styledText(.keyword, "usage"));
72 try parts.append(builder.allocator, try builder.punct(":"));
73 try parts.append(builder.allocator, pretty.hardline);
74 for (usage_commands) |command| {
75 try parts.append(builder.allocator, try builder.spaces(2));
76 try parts.append(builder.allocator, try builder.nest(4, try builder.styledWords(.name, command)));
77 try parts.append(builder.allocator, pretty.hardline);
78 }
79 return try builder.concat(parts.items);
80 }
81
82 test "usage renders through pretty plain and colored output" {
83 const plain = try renderAlloc(std.testing.allocator, .{ .width = 88 });
84 defer std.testing.allocator.free(plain);
85 try std.testing.expect(std.mem.indexOf(u8, plain, "\x1b[") == null);
86 try std.testing.expect(std.mem.indexOf(u8, plain, "usage:\n") != null);
87 try std.testing.expect(std.mem.indexOf(u8, plain, "tracy-agent summary <events.jsonl>") != null);
88 try std.testing.expect(std.mem.indexOf(u8, plain, "--budget-ns") != null);
89 try std.testing.expect(std.mem.indexOf(u8, plain, "--min-gpu-delta-ns") != null);
90 try std.testing.expect(std.mem.indexOf(u8, plain, "--frame-budget-ns") != null);
91 try std.testing.expect(
92 std.mem.indexOf(u8, plain, "--min-memory-lifetime-delta-ns") != null,
93 );
94 try std.testing.expect(std.mem.indexOf(u8, plain, "gpu|gpu-tail|cpu|cpu-tail") != null);
95
96 const colored = try renderAlloc(std.testing.allocator, .{ .width = 88, .color = .ansi });
97 defer std.testing.allocator.free(colored);
98 try std.testing.expect(std.mem.indexOf(u8, colored, "\x1b[") != null);
99 }
100
101 test "usage wraps long tracy commands at terminal width" {
102 const rendered = try renderAlloc(std.testing.allocator, .{ .width = 48 });
103 defer std.testing.allocator.free(rendered);
104 try std.testing.expect(std.mem.indexOf(u8, rendered, "tracy-agent locks <events.jsonl> [--format\n") != null);
105 }
106
107 test "errors render through pretty plain and colored output" {
108 const plain = try pretty_usage.renderErrorTextAlloc(
109 std.testing.allocator,
110 "tracy-agent",
111 "UnknownCommand",
112 .{ .layout = .{ .width = 88 } },
113 );
114 defer std.testing.allocator.free(plain);
115 try std.testing.expectEqualStrings("tracy-agent: UnknownCommand\n", plain);
116
117 const colored = try pretty_usage.renderErrorTextAlloc(
118 std.testing.allocator,
119 "tracy-agent",
120 "UnknownCommand",
121 .{ .layout = .{ .width = 88, .color = .ansi } },
122 );
123 defer std.testing.allocator.free(colored);
124 try std.testing.expect(std.mem.indexOf(u8, colored, "\x1b[") != null);
125 try std.testing.expect(std.mem.indexOf(u8, colored, "UnknownCommand") != null);
126 }