v0.3.0 99dc266

options_manifest.go RawHistoryFolder

1//go:build linux
2
3package main
4
5import (
6	"encoding/json"
7	"errors"
8	"flag"
9	"fmt"
10	"io"
11	"os"
12	"path/filepath"
13)
14
15var (
16	canonicalTopologyIDs = []string{"1x4", "4x1", "4x4", "8x1"}
17	canonicalTopologySet = stringSet(canonicalTopologyIDs)
18	canonicalWorkloadIDs = []string{
19		"idle", "active_output", "all_output", "inactive_output", "interactive_flood",
20		"copy_search", "resize_sweep", "snapshot_output_resize", "attach_restore_tab_switch",
21	}
22	canonicalWorkloadSet = stringSet(canonicalWorkloadIDs)
23)
24
25func stringSet(ids []string) map[string]bool {
26	set := make(map[string]bool, len(ids))
27	for _, id := range ids {
28		set[id] = true
29	}
30	return set
31}
32
33func parseOptions(args []string) (options, error) {
34	var o options
35	fs := flag.NewFlagSet("vev-perf-harness", flag.ContinueOnError)
36	fs.SetOutput(io.Discard)
37	fs.StringVar(&o.vevBin, "vev-bin", "", "public vev binary")
38	fs.StringVar(&o.manifest, "manifest", "", "manifest")
39	fs.StringVar(&o.out, "out", "", "output directory")
40	fs.StringVar(&o.scenario, "scenario", "", "one canonical scenario ID (after full manifest validation)")
41	fs.DurationVar(&o.warmup, "warmup", 0, "warmup")
42	fs.DurationVar(&o.duration, "duration", 0, "measurement duration")
43	fs.IntVar(&o.repetitions, "repetitions", 0, "repetitions")
44	if err := fs.Parse(args); err != nil {
45		return o, err
46	}
47	seen := map[string]bool{}
48	fs.Visit(func(f *flag.Flag) { seen[f.Name] = true })
49	for _, name := range []string{"vev-bin", "manifest", "out", "warmup", "duration", "repetitions"} {
50		if !seen[name] {
51			return o, fmt.Errorf("--%s is required", name)
52		}
53	}
54	if o.vevBin == "" || o.manifest == "" || o.out == "" {
55		return o, errors.New("--vev-bin, --manifest, and --out are required")
56	}
57	if o.warmup < 0 {
58		return o, errors.New("--warmup must not be negative")
59	}
60	if o.duration < minimumDuration {
61		return o, fmt.Errorf("--duration must be at least %s", minimumDuration)
62	}
63	if o.repetitions < minimumRepetitions {
64		return o, fmt.Errorf("--repetitions must be at least %d", minimumRepetitions)
65	}
66	return o, nil
67}
68
69// resolvePathOptions captures the harness cwd once, before role launchers
70// assign their isolated working directories. All downstream filesystem access,
71// manifests, and process environments consequently refer to the same paths.
72func resolvePathOptions(o options) (options, error) {
73	cwd, err := os.Getwd()
74	if err != nil {
75		return o, fmt.Errorf("resolve harness working directory: %w", err)
76	}
77	absolute := func(path string) string {
78		if filepath.IsAbs(path) {
79			return filepath.Clean(path)
80		}
81		return filepath.Clean(filepath.Join(cwd, path))
82	}
83	o.vevBin = absolute(o.vevBin)
84	o.manifest = absolute(o.manifest)
85	o.out = absolute(o.out)
86	return o, nil
87}
88
89func readManifest(path string) (manifest, error) {
90	f, err := os.Open(path)
91	if err != nil {
92		return manifest{}, err
93	}
94	var m manifest
95	if err := json.NewDecoder(f).Decode(&m); err != nil {
96		return manifest{}, closeFile(f, err)
97	}
98	if err := f.Close(); err != nil {
99		return manifest{}, err
100	}
101	return m, nil
102}
103
104func validateManifest(m manifest) error {
105	if m.Schema != 1 {
106		return errors.New("unsupported manifest schema")
107	}
108	tops := map[string]bool{}
109	for _, t := range m.Topologies {
110		if t.ID == "" {
111			return errors.New("empty topology id")
112		}
113		if tops[t.ID] {
114			return fmt.Errorf("duplicate topology id %q", t.ID)
115		}
116		if !canonicalTopologySet[t.ID] {
117			return fmt.Errorf("unknown topology id %q", t.ID)
118		}
119		if t.Geometry != "120x40" || t.RowsPerPane != 10000 {
120			return fmt.Errorf("invalid topology %q", t.ID)
121		}
122		tops[t.ID] = true
123	}
124	for _, v := range canonicalTopologyIDs {
125		if !tops[v] {
126			return fmt.Errorf("missing canonical topology %s", v)
127		}
128	}
129	works := map[string]bool{}
130	for _, workload := range m.Workloads {
131		if workload == "" {
132			return errors.New("empty workload id")
133		}
134		if works[workload] {
135			return fmt.Errorf("duplicate workload id %q", workload)
136		}
137		if !canonicalWorkloadSet[workload] {
138			return fmt.Errorf("unknown workload id %q", workload)
139		}
140		works[workload] = true
141	}
142	for _, v := range canonicalWorkloadIDs {
143		if !works[v] {
144			return fmt.Errorf("missing canonical workload %s", v)
145		}
146	}
147	trans := map[string]bool{}
148	for _, t := range m.Transports {
149		if t.ID == "" {
150			return errors.New("empty transport id")
151		}
152		if trans[t.ID] {
153			return fmt.Errorf("duplicate transport id %q", t.ID)
154		}
155		if err := validateTransportFixture(t); err != nil {
156			return err
157		}
158		trans[t.ID] = true
159	}
160	for _, v := range []string{"local", "ssh_stdio", "udp_baseline", "udp_25ms", "udp_100ms", "udp_loss_0pct", "udp_loss_1pct"} {
161		if !trans[v] {
162			return fmt.Errorf("missing canonical transport %s", v)
163		}
164	}
165	seen := map[string]bool{}
166	covered := map[string]bool{}
167	for _, s := range m.Scenarios {
168		if s.ID == "" || seen[s.ID] {
169			return fmt.Errorf("duplicate or empty scenario id %q", s.ID)
170		}
171		seen[s.ID] = true
172		if !tops[s.Topology] || !works[s.Workload] || !trans[s.Transport] {
173			return fmt.Errorf("scenario %s references unknown matrix entry", s.ID)
174		}
175		if s.InapplicableReason == "" && len(s.Roles) == 0 {
176			return fmt.Errorf("scenario %s has no process roles", s.ID)
177		}
178		if s.InapplicableReason != "" && len(s.Roles) != 0 {
179			return fmt.Errorf("inapplicable scenario %s must not launch roles", s.ID)
180		}
181		if s.InapplicableReason == "" && !equalRoleSet(s.Roles, requiredRoles(s.Transport)) {
182			return fmt.Errorf("scenario %s roles do not route transport %s", s.ID, s.Transport)
183		}
184		covered[s.Topology+"\x00"+s.Workload+"\x00"+s.Transport] = true
185	}
186	for topologyID := range tops {
187		for workload := range works {
188			for transportID := range trans {
189				key := topologyID + "\x00" + workload + "\x00" + transportID
190				if !covered[key] {
191					return fmt.Errorf("missing scenario or inapplicable reason for %s/%s/%s", topologyID, workload, transportID)
192				}
193			}
194		}
195	}
196	return nil
197}
198
199// selectScenario is intentionally called only after validateManifest. A bounded
200// local evidence run therefore cannot mask an incomplete canonical matrix.
201func selectScenario(m manifest, id string) ([]scenario, error) {
202	for _, s := range m.Scenarios {
203		if s.ID != id {
204			continue
205		}
206		if s.InapplicableReason != "" {
207			return nil, fmt.Errorf("scenario %s is inapplicable: %s", id, s.InapplicableReason)
208		}
209		return []scenario{s}, nil
210	}
211	return nil, fmt.Errorf("unknown canonical scenario %s", id)
212}
213
214func requiredRoles(transportID string) []string {
215	switch transportID {
216	case "local":
217		return []string{"daemon", "client"}
218	case "ssh_stdio":
219		return []string{"daemon", "client", "ssh_stdio_peer"}
220	default:
221		return []string{"daemon", "client", "udp_peer"}
222	}
223}
224
225func equalRoleSet(got, want []string) bool {
226	if len(got) != len(want) {
227		return false
228	}
229	seen := make(map[string]bool, len(got))
230	for _, role := range got {
231		seen[role] = true
232	}
233	for _, role := range want {
234		if !seen[role] {
235			return false
236		}
237	}
238	return true
239}
240
241func validateTransportFixture(t transport) error {
242	want := map[string]transport{
243		"local":         {ID: "local", Kind: "local"},
244		"ssh_stdio":     {ID: "ssh_stdio", Kind: "ssh_stdio"},
245		"udp_baseline":  {ID: "udp_baseline", Kind: "udp"},
246		"udp_25ms":      {ID: "udp_25ms", Kind: "udp", RTTMS: 25},
247		"udp_100ms":     {ID: "udp_100ms", Kind: "udp", RTTMS: 100},
248		"udp_loss_0pct": {ID: "udp_loss_0pct", Kind: "udp", LossPercent: 0},
249		"udp_loss_1pct": {ID: "udp_loss_1pct", Kind: "udp", LossPercent: 1},
250	}
251	w, ok := want[t.ID]
252	if !ok || t.Kind != w.Kind || t.RTTMS != w.RTTMS || t.LossPercent != w.LossPercent {
253		return fmt.Errorf("invalid canonical transport fixture %+v", t)
254	}
255	return nil
256}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close