internal/literals: add benchmark to measure the run-time overhead
parent
89facf1648
commit
9a8608f061
@ -0,0 +1,31 @@
|
||||
//go:build garble_testing
|
||||
|
||||
package literals
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
obfMapEnv := os.Getenv("GARBLE_TEST_LITERALS_OBFUSCATOR_MAP")
|
||||
if obfMapEnv == "" {
|
||||
panic("literals obfuscator map required for testing build")
|
||||
}
|
||||
testPkgToObfuscatorMap = make(map[string]obfuscator)
|
||||
|
||||
// Parse obfuscator mapping: pkgName1=obfIndex1,pkgName2=obfIndex2
|
||||
pairs := strings.Split(obfMapEnv, ",")
|
||||
for _, pair := range pairs {
|
||||
keyValue := strings.SplitN(pair, "=", 2)
|
||||
|
||||
pkgName := keyValue[0]
|
||||
obfIndex, err := strconv.Atoi(keyValue[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testPkgToObfuscatorMap[pkgName] = Obfuscators[obfIndex]
|
||||
}
|
||||
TestObfuscator = obfMapEnv
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
// This script generate benchmarks for performance analysis of individual obfuscator literals.
|
||||
// Note that only the speed of obfuscated methods is measured, initialization cost or build speed are not measured.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"mvdan.cc/garble/internal/literals"
|
||||
)
|
||||
|
||||
const (
|
||||
minDataLen = 8
|
||||
maxDataLen = 64
|
||||
stepDataLen = 4
|
||||
dataCountPerLen = 10
|
||||
moduleName = "test/literals"
|
||||
garbleSeed = "o9WDTZ4CN4w"
|
||||
|
||||
// For benchmarking individual obfuscators, we use package=obfuscator mapping
|
||||
// and add a prefix to package name to make sure there are no collisions with system packages.
|
||||
packagePrefix = "literals_bench_"
|
||||
)
|
||||
|
||||
func generateRunSrc() string {
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(`
|
||||
var alwaysFalseFlag = false
|
||||
|
||||
func noop(i interface{}) {
|
||||
if alwaysFalseFlag {
|
||||
println(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Run() {
|
||||
`)
|
||||
|
||||
dataStr := strings.Repeat("X", maxDataLen)
|
||||
dataBytes := make([]string, maxDataLen)
|
||||
for i := 0; i < len(dataBytes); i++ {
|
||||
dataBytes[i] = strconv.Itoa(i)
|
||||
}
|
||||
|
||||
for dataLen := minDataLen; dataLen <= maxDataLen; dataLen += stepDataLen {
|
||||
for y := 0; y < dataCountPerLen; y++ {
|
||||
fmt.Fprintf(&sb, "\tnoop(%q)\n", dataStr[:dataLen])
|
||||
fmt.Fprintf(&sb, "\tnoop([]byte{%s})\n", strings.Join(dataBytes[:dataLen], ", "))
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString("}\n")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func buildTestGarble(tdir string) string {
|
||||
garbleBin := filepath.Join(tdir, "garble")
|
||||
if runtime.GOOS == "windows" {
|
||||
garbleBin += ".exe"
|
||||
}
|
||||
|
||||
output, err := exec.Command("go", "build", "-tags", "garble_testing", "-o="+garbleBin).CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatalf("garble build failed: %v\n%s", err, string(output))
|
||||
}
|
||||
|
||||
return garbleBin
|
||||
}
|
||||
|
||||
func handle(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeDateFile(tdir, obfName, src string) {
|
||||
pkgName := packagePrefix + obfName
|
||||
|
||||
var sb strings.Builder
|
||||
fmt.Fprintf(&sb, "package %s\n\n", pkgName)
|
||||
sb.WriteString(src)
|
||||
|
||||
dir := filepath.Join(tdir, pkgName)
|
||||
handle(os.MkdirAll(dir, 0o777))
|
||||
handle(os.WriteFile(filepath.Join(dir, "data.go"), []byte(sb.String()), 0o777))
|
||||
}
|
||||
|
||||
func writeTestFile(dir, obfName string) {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(`package main
|
||||
import "testing"
|
||||
`)
|
||||
pkgName := packagePrefix + obfName
|
||||
fmt.Fprintf(&sb, "import %q\n", moduleName+"/"+pkgName)
|
||||
fmt.Fprintf(&sb, `func Benchmark%s(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
%s.Run()
|
||||
}
|
||||
}
|
||||
`, strings.ToUpper(obfName[:1])+obfName[1:], pkgName)
|
||||
|
||||
handle(os.WriteFile(filepath.Join(dir, obfName+"_test.go"), []byte(sb.String()), 0o777))
|
||||
}
|
||||
|
||||
func main() {
|
||||
tdir, err := os.MkdirTemp("", "literals-bench*")
|
||||
if err != nil {
|
||||
log.Fatalf("create temp directory failed: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tdir)
|
||||
|
||||
if err := os.WriteFile(filepath.Join(tdir, "go.mod"), []byte("module "+moduleName), 0o777); err != nil {
|
||||
log.Fatalf("write go.mod failed: %v", err)
|
||||
}
|
||||
|
||||
runSrc := generateRunSrc()
|
||||
writeTest := func(name string) {
|
||||
writeDateFile(tdir, name, runSrc)
|
||||
writeTestFile(tdir, name)
|
||||
}
|
||||
|
||||
var packageToObfuscatorIndex []string
|
||||
for i, obf := range literals.Obfuscators {
|
||||
obfName := reflect.TypeOf(obf).Name()
|
||||
writeTest(obfName)
|
||||
packageToObfuscatorIndex = append(packageToObfuscatorIndex, fmt.Sprintf(packagePrefix+"%s=%d", obfName, i))
|
||||
}
|
||||
writeTest("all")
|
||||
|
||||
garbleBin := buildTestGarble(tdir)
|
||||
args := append([]string{"-seed", garbleSeed, "-literals", "test", "-bench"}, os.Args[1:]...)
|
||||
cmd := exec.Command(garbleBin, args...)
|
||||
cmd.Env = append(os.Environ(),
|
||||
// Explicitly specify package for obfuscation to avoid affecting testing package.
|
||||
"GOGARBLE="+moduleName,
|
||||
"GARBLE_TEST_LITERALS_OBFUSCATOR_MAP="+strings.Join(packageToObfuscatorIndex, ","),
|
||||
)
|
||||
cmd.Dir = tdir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
os.Exit(exitErr.ExitCode())
|
||||
}
|
||||
log.Fatalf("run garble test failed: %v", err)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue