Separate out rule optimizers from main syscall rendering.

This does an optimization pass over `RuleSet`s prior to rendering anything,
rather than optimizing at the last minute for each syscall.

PiperOrigin-RevId: 581671748
This commit is contained in:
Etienne Perot
2023-11-12 00:47:32 -08:00
committed by gVisor bot
parent a69a4835f2
commit c46ffacf2f
3 changed files with 32 additions and 11 deletions
+26 -8
View File
@@ -330,15 +330,32 @@ type BuildStats struct {
// BPF bytecode optimizations).
BuildDuration time.Duration
// OptimizeDuration is the amount of time it took to run BPF bytecode
// RuleOptimizeDuration is the amount of time it took to run SyscallRule
// optimizations.
OptimizeDuration time.Duration
RuleOptimizeDuration time.Duration
// BPFOptimizeDuration is the amount of time it took to run BPF bytecode
// optimizations.
BPFOptimizeDuration time.Duration
}
// BuildProgram builds a BPF program from the given map of actions to matching
// SyscallRules. The single generated program covers all provided RuleSets.
func BuildProgram(rules []RuleSet, options ProgramOptions) ([]bpf.Instruction, BuildStats, error) {
start := time.Now()
// Make a copy of the syscall rules and optimize them.
rulesCopy := make([]RuleSet, len(rules))
for i, rs := range rules {
rulesMap := MakeSyscallRules(make(map[uintptr]SyscallRule, len(rs.Rules.rules)))
for sysno, rule := range rs.Rules.rules {
rulesMap.rules[sysno] = optimizeSyscallRule(rule)
}
rs.Rules = rulesMap
rulesCopy[i] = rs
}
ruleOptimizeDuration := time.Since(start)
program := &syscallProgram{
program: bpf.NewProgramBuilder(),
}
@@ -350,7 +367,7 @@ func BuildProgram(rules []RuleSet, options ProgramOptions) ([]bpf.Instruction, B
badArchLabel := label("badarch")
program.Stmt(bpf.Ld|bpf.Abs|bpf.W, seccompDataOffsetArch)
program.IfNot(bpf.Jmp|bpf.Jeq|bpf.K, LINUX_AUDIT_ARCH, badArchLabel)
if err := buildIndex(rules, program); err != nil {
if err := buildIndex(rulesCopy, program); err != nil {
return nil, BuildStats{}, err
}
@@ -367,16 +384,17 @@ func BuildProgram(rules []RuleSet, options ProgramOptions) ([]bpf.Instruction, B
return nil, BuildStats{}, err
}
beforeOpt := len(insns)
buildDuration := time.Since(start)
buildDuration := time.Since(start) - ruleOptimizeDuration
insns = bpf.Optimize(insns)
optimizeDuration := time.Since(start) - buildDuration
bpfOptimizeDuration := time.Since(start) - ruleOptimizeDuration - buildDuration
afterOpt := len(insns)
log.Debugf("Seccomp program optimized from %d to %d instructions; took %v to build and %v to optimize", beforeOpt, afterOpt, buildDuration, optimizeDuration)
log.Debugf("Seccomp program optimized from %d to %d instructions; took %v to build and %v to optimize", beforeOpt, afterOpt, buildDuration, bpfOptimizeDuration)
return insns, BuildStats{
SizeBeforeOptimizations: beforeOpt,
SizeAfterOptimizations: afterOpt,
BuildDuration: buildDuration,
OptimizeDuration: optimizeDuration,
RuleOptimizeDuration: ruleOptimizeDuration,
BPFOptimizeDuration: bpfOptimizeDuration,
}, nil
}
@@ -532,7 +550,7 @@ func buildBSTProgram(n *node, rng knownRange, rules []RuleSet, program *syscallP
// check the next rule set. We need to ensure
// that at the very end, we insert a direct
// jump label for the unmatched case.
optimizeSyscallRule(rule).Render(program, ruleSetLabelSet)
rule.Render(program, ruleSetLabelSet)
ruleSetFrag.MustHaveJumpedTo(ruleSetLabelSet.Matched(), ruleSetLabelSet.Mismatched())
program.Label(ruleSetLabelSet.Matched())
program.Ret(rs.Action)
+3 -2
View File
@@ -60,8 +60,9 @@ func main() {
log.Infof("Size before optimizations: %d", stats.SizeBeforeOptimizations)
log.Infof("Size after optimizations: %d", stats.SizeAfterOptimizations)
log.Infof("Build duration: %v", stats.BuildDuration)
log.Infof("Optimization passes duration: %v", stats.OptimizeDuration)
log.Infof("Total duration: %v", stats.BuildDuration+stats.OptimizeDuration)
log.Infof("Rule optimization passes duration: %v", stats.RuleOptimizeDuration)
log.Infof("BPF optimization passes duration: %v", stats.BPFOptimizeDuration)
log.Infof("Total duration: %v", stats.BuildDuration+stats.RuleOptimizeDuration+stats.BPFOptimizeDuration)
switch *output {
case "fancy":
dump, err := bpf.DecodeInstructions(insns)
+3 -1
View File
@@ -149,7 +149,9 @@ func RunBench(b *testing.B, bn secbenchdef.Bench) {
b.Fatalf("program does not compile: %v", err)
}
b.ReportMetric(float64(bn.BuildStats.BuildDuration.Nanoseconds()), "build-ns")
b.ReportMetric(float64(bn.BuildStats.OptimizeDuration.Nanoseconds()), "opt-ns")
b.ReportMetric(float64(bn.BuildStats.RuleOptimizeDuration.Nanoseconds()), "ruleopt-ns")
b.ReportMetric(float64(bn.BuildStats.BPFOptimizeDuration.Nanoseconds()), "bpfopt-ns")
b.ReportMetric(float64((bn.BuildStats.RuleOptimizeDuration + bn.BuildStats.BPFOptimizeDuration).Nanoseconds()), "opt-ns")
b.ReportMetric(float64(bn.BuildStats.SizeBeforeOptimizations), "gen-instr")
b.ReportMetric(float64(bn.BuildStats.SizeAfterOptimizations), "opt-instr")
b.ReportMetric(float64(bn.BuildStats.SizeBeforeOptimizations)/float64(bn.BuildStats.SizeAfterOptimizations), "compression-ratio")